LearnOpenGL

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 2df7430127146b508a664d59daddfae32daa19f2
parent f5b0afda7973758cd1c6c003bd84ea724118dd46
Author: Kenji Matsuda <ftvda283@gmail.com>
Date:   Tue, 26 Oct 2021 19:57:48 +0900

make style

Diffstat:
Abin/blog_update | 5+++++
Abin/update | 9+++++++++
Mman/About.html | 273-------------------------------------------------------------------------------
Mman/Advanced-Lighting/Advanced-Lighting.html | 273-------------------------------------------------------------------------------
Mman/Advanced-Lighting/Bloom.html | 273-------------------------------------------------------------------------------
Mman/Advanced-Lighting/Deferred-Shading.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/Gamma-Correction.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/HDR.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/Normal-Mapping.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/Parallax-Mapping.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/SSAO.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/Shadows/Point-Shadows.html | 258+------------------------------------------------------------------------------
Mman/Advanced-Lighting/Shadows/Shadow-Mapping.html | 258+------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Advanced-Data.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Advanced-GLSL.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Anti-Aliasing.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Blending.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Cubemaps.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Depth-testing.html | 273-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Face-culling.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Framebuffers.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Geometry-Shader.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Instancing.html | 272-------------------------------------------------------------------------------
Mman/Advanced-OpenGL/Stencil-testing.html | 272-------------------------------------------------------------------------------
Mman/Code-repository.html | 273-------------------------------------------------------------------------------
Mman/Getting-started/Camera.html | 12------------
Mman/Getting-started/Coordinate-Systems.html | 12------------
Mman/Getting-started/Creating-a-window.html | 12------------
Mman/Getting-started/Hello-Triangle.html | 12------------
Mman/Getting-started/Hello-Window.html | 12------------
Mman/Getting-started/OpenGL.html | 12------------
Mman/Getting-started/Review.html | 12------------
Mman/Getting-started/Shaders.html | 12------------
Mman/Getting-started/Textures.html | 12------------
Mman/Getting-started/Transformations.html | 12------------
Mman/Guest-Articles/2020/OIT/Introduction.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/2020/OIT/Weighted-Blended.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/2020/Skeletal-Animation.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/2021/CSM.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/2021/Scene/Frustum-Culling.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/2021/Scene/Scene-Graph.html | 272-------------------------------------------------------------------------------
Mman/Guest-Articles/How-to-publish.html | 272-------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Audio.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Breakout.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Collisions/Ball.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Collisions/Collision-detection.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Collisions/Collision-resolution.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Final-thoughts.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Levels.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Particles.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Postprocessing.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Powerups.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Render-text.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Rendering-Sprites.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/2D-Game/Setting-up.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/Debugging.html | 258+------------------------------------------------------------------------------
Mman/In-Practice/Text-Rendering.html | 258+------------------------------------------------------------------------------
Mman/Introduction.html | 11-----------
Mman/Lighting/Basic-Lighting.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Colors.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Light-casters.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Lighting-maps.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Materials.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Multiple-lights.html | 273-------------------------------------------------------------------------------
Mman/Lighting/Review.html | 273-------------------------------------------------------------------------------
Mman/Model-Loading/Assimp.html | 272-------------------------------------------------------------------------------
Mman/Model-Loading/Mesh.html | 272-------------------------------------------------------------------------------
Mman/Model-Loading/Model.html | 272-------------------------------------------------------------------------------
Mman/PBR/IBL/Diffuse-irradiance.html | 273-------------------------------------------------------------------------------
Mman/PBR/IBL/Specular-IBL.html | 273-------------------------------------------------------------------------------
Mman/PBR/Lighting.html | 273-------------------------------------------------------------------------------
Mman/PBR/Theory.html | 273-------------------------------------------------------------------------------
Mman/Translations.html | 258+------------------------------------------------------------------------------
Mman/static/style.css | 12++++++++++++
Apub/About.html | 348+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Advanced-Lighting.html | 438+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Bloom.html | 661+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Deferred-Shading.html | 844+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Gamma-Correction.html | 551+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/HDR.html | 553+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Normal-Mapping.html | 858+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Parallax-Mapping.html | 729+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/SSAO.html | 954+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Shadows/Point-Shadows.html | 926+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-Lighting/Shadows/Shadow-Mapping.html | 1047+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Advanced-Data.html | 460+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Advanced-GLSL.html | 952+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Anti-Aliasing.html | 608+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Blending.html | 740+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Cubemaps.html | 878+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Depth-testing.html | 619+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Face-culling.html | 468+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Framebuffers.html | 899+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Geometry-Shader.html | 948+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Instancing.html | 763+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Advanced-OpenGL/Stencil-testing.html | 630+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Code-repository.html | 340+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Camera.html | 988+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Coordinate-Systems.html | 884+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Creating-a-window.html | 607+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Hello-Triangle.html | 1156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Hello-Window.html | 634+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/OpenGL.html | 474+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Review.html | 397+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Shaders.html | 1014+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Textures.html | 954+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Getting-started/Transformations.html | 1018+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2020/OIT/Introduction.html | 486+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2020/OIT/Weighted-Blended.html | 870+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2020/Skeletal-Animation.html | 1207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2021/CSM.html | 787+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2021/Scene/Frustum-Culling.html | 838+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/2021/Scene/Scene-Graph.html | 774+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Guest-Articles/How-to-publish.html | 387+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Audio.html | 471+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Breakout.html | 408+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Collisions/Ball.html | 522+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Collisions/Collision-detection.html | 559+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Collisions/Collision-resolution.html | 635+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Final-thoughts.html | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Levels.html | 687+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Particles.html | 630+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Postprocessing.html | 609+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Powerups.html | 734+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Render-text.html | 748+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Rendering-Sprites.html | 604++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/2D-Game/Setting-up.html | 485+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/Debugging.html | 888+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/In-Practice/Text-Rendering.html | 758+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Introduction.html | 410+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Basic-Lighting.html | 712+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Colors.html | 542+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Light-casters.html | 869+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Lighting-maps.html | 532+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Materials.html | 541+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Multiple-lights.html | 589+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Lighting/Review.html | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Model-Loading/Assimp.html | 412+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Model-Loading/Mesh.html | 562+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Model-Loading/Model.html | 748+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/PBR/IBL/Diffuse-irradiance.html | 992+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/PBR/IBL/Specular-IBL.html | 1174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/PBR/Lighting.html | 744+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/PBR/Theory.html | 925+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/Translations.html | 389+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/img/getting-started/camera_axes.png | 0
Apub/img/getting-started/camera_pitch.png | 0
Apub/img/getting-started/camera_pitch_yaw_roll.png | 0
Apub/img/getting-started/camera_triangle.png | 0
Apub/img/getting-started/camera_yaw.png | 0
Apub/img/getting-started/cmake.png | 0
Apub/img/getting-started/coordinate_systems.png | 0
Apub/img/getting-started/coordinate_systems_multiple_objects.png | 0
Apub/img/getting-started/coordinate_systems_result.png | 0
Apub/img/getting-started/coordinate_systems_right_handed.png | 0
Apub/img/getting-started/filter_linear.png | 0
Apub/img/getting-started/filter_nearest.png | 0
Apub/img/getting-started/glm.png | 0
Apub/img/getting-started/hellotriangle.png | 0
Apub/img/getting-started/hellotriangle2.png | 0
Apub/img/getting-started/hellowindow.png | 0
Apub/img/getting-started/hellowindow2.png | 0
Apub/img/getting-started/include_directories.png | 0
Apub/img/getting-started/linker_input.png | 0
Apub/img/getting-started/matrix_multiplication.png | 0
Apub/img/getting-started/mipmaps.png | 0
Apub/img/getting-started/ndc.png | 0
Apub/img/getting-started/opengl.jpg | 0
Apub/img/getting-started/orthographic_frustum.png | 0
Apub/img/getting-started/perspective.png | 0
Apub/img/getting-started/perspective_frustum.png | 0
Apub/img/getting-started/perspective_orthographic.png | 0
Apub/img/getting-started/pipeline.png | 0
Apub/img/getting-started/shader2.png | 0
Apub/img/getting-started/shaders.png | 0
Apub/img/getting-started/shaders3.png | 0
Apub/img/getting-started/start_video.png | 0
Apub/img/getting-started/tex_coords.png | 0
Apub/img/getting-started/texture_filtering.png | 0
Apub/img/getting-started/texture_wrapping.png | 0
Apub/img/getting-started/textures.png | 0
Apub/img/getting-started/textures2.png | 0
Apub/img/getting-started/textures_combined.png | 0
Apub/img/getting-started/textures_combined2.png | 0
Apub/img/getting-started/textures_funky.png | 0
Apub/img/getting-started/transformations.png | 0
Apub/img/getting-started/vc_directories.png | 0
Apub/img/getting-started/vectors.png | 0
Apub/img/getting-started/vectors_addition.png | 0
Apub/img/getting-started/vectors_angle.png | 0
Apub/img/getting-started/vectors_crossproduct.png | 0
Apub/img/getting-started/vectors_scale.png | 0
Apub/img/getting-started/vectors_subtraction.png | 0
Apub/img/getting-started/vectors_triangle.png | 0
Apub/img/getting-started/vertex_array_objects.png | 0
Apub/img/getting-started/vertex_array_objects_ebo.png | 0
Apub/img/getting-started/vertex_attribute_pointer.png | 0
Apub/img/getting-started/vertex_attribute_pointer_interleaved.png | 0
Apub/img/getting-started/vertex_attribute_pointer_interleaved_textures.png | 0
Apub/img/getting-started/x64.png | 0
Apub/img/start_video.png | 0
Apub/img/textures/awesomeface.png | 0
Apub/static/functions.js | 7+++++++
Apub/static/mathjax.js | 19+++++++++++++++++++
Apub/static/style.css | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apub/video/getting-started/camera_circle.mp4 | 0
Apub/video/getting-started/camera_mouse.mp4 | 0
Apub/video/getting-started/camera_smooth.mp4 | 0
Apub/video/getting-started/coordinate_system_depth.mp4 | 0
Apub/video/getting-started/coordinate_system_no_depth.mp4 | 0
Apub/video/getting-started/shaders.mp4 | 0
Apub/video/getting-started/transformations.mp4 | 0
Atemplates/base.html | 321+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
213 files changed, 49997 insertions(+), 16107 deletions(-)

diff --git a/bin/blog_update b/bin/blog_update @@ -0,0 +1,5 @@ +#!/bin/sh +cd $(dirname $0)/.. + +grep "\<h1\>" man/blog/posts/*.html | sort -r | sed 's/^.*blog\///;s/:<h1>/ /;s/<\/h1>//' | +awk '{print $1,$1,$2}' | sed 's/^[^ 0-9]*\([0-9]*\)[^ ]*/\1/' | awk 'BEGIN{print "<h1>日記</h1><ul>"}END{print "</ul>"}{printf "<li>[%s]<a href=\"%s\">%s</a></li>\n",$1,$2,$3}' > man/blog/blog_index.html diff --git a/bin/update b/bin/update @@ -0,0 +1,9 @@ +#!/bin/sh +cd $(dirname $0)/.. + +# apply template to html files +find man -type f | grep html$ | sed 's;^man/;;' | + xargs -I@ sh -c 'f=@; mkdir -p pub/$(dirname $f); sed -n "/<\!--content-->/!p; /<\!--content-->/r man/@" templates/base.html > pub/@' + +# copy non-html files +find man -type f | grep -v html$ | sed 's;^man/;;' | xargs -I@ sh -c 'f=@; mkdir -p pub/$(dirname $f); cp man/@ pub/@' diff --git a/man/About.html b/man/About.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - About</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">About</h1> <h1 id="content-url" style='display:none;'>About</h1> <p> @@ -281,20 +26,3 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-Lighting/Advanced-Lighting.html b/man/Advanced-Lighting/Advanced-Lighting.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Advanced Lighting</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Advanced Lighting</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Advanced-Lighting</h1> <p> @@ -371,20 +116,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-Lighting/Bloom.html b/man/Advanced-Lighting/Bloom.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Bloom</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Bloom</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Bloom</h1> <p> @@ -594,20 +339,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-Lighting/Deferred-Shading.html b/man/Advanced-Lighting/Deferred-Shading.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Deferred Shading</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Deferred Shading</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Deferred-Shading</h1> <p> @@ -776,4 +521,4 @@ Future Rendering Pipelines</a>: slides by Andrew Lauritzen discussing high-level </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/Gamma-Correction.html b/man/Advanced-Lighting/Gamma-Correction.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Gamma Correction</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Gamma Correction</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Gamma-Correction</h1> <p> @@ -483,4 +228,4 @@ float attenuation = 1.0 / distance; </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/HDR.html b/man/Advanced-Lighting/HDR.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - HDR</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">HDR</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/HDR</h1> <p> @@ -485,4 +230,4 @@ void main() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/Normal-Mapping.html b/man/Advanced-Lighting/Normal-Mapping.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Normal Mapping</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Normal Mapping</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Normal-Mapping</h1> <p> @@ -790,4 +535,4 @@ mat3 TBN = mat3(T, B, N) </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/Parallax-Mapping.html b/man/Advanced-Lighting/Parallax-Mapping.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Parallax Mapping</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Parallax Mapping</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Parallax-Mapping</h1> <p> @@ -661,4 +406,4 @@ return finalTexCoords; </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/SSAO.html b/man/Advanced-Lighting/SSAO.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - SSAO</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">SSAO</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/SSAO</h1> <p> @@ -886,4 +631,4 @@ FragColor = pow(occlusion, power); </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/Shadows/Point-Shadows.html b/man/Advanced-Lighting/Shadows/Point-Shadows.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Point Shadows</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Point Shadows</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Shadows/Point-Shadows</h1> <p> @@ -858,4 +603,4 @@ float diskRadius = (1.0 + (viewDistance / far_plane)) / 25.0; </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-Lighting/Shadows/Shadow-Mapping.html b/man/Advanced-Lighting/Shadows/Shadow-Mapping.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Shadow Mapping</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Shadow Mapping</h1> <h1 id="content-url" style='display:none;'>Advanced-Lighting/Shadows/Shadow-Mapping</h1> <p> @@ -979,4 +724,4 @@ void main() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Advanced-OpenGL/Advanced-Data.html b/man/Advanced-OpenGL/Advanced-Data.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Advanced Data</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Advanced Data</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Advanced-Data</h1> @@ -392,20 +138,3 @@ float vertexData[] = { ... }; </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Advanced-GLSL.html b/man/Advanced-OpenGL/Advanced-GLSL.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Advanced GLSL</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Advanced GLSL</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Advanced-GLSL</h1> @@ -884,20 +630,3 @@ shaderRed.setMat4("model", model); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Anti-Aliasing.html b/man/Advanced-OpenGL/Anti-Aliasing.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Anti Aliasing</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Anti Aliasing</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Anti-Aliasing</h1> @@ -540,20 +286,3 @@ vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3); // 4th subsample </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Blending.html b/man/Advanced-OpenGL/Blending.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Blending</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Blending</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Blending</h1> @@ -672,20 +418,3 @@ for(std::map&lt;float,glm::vec3&gt;::reverse_iterator it = sorted.rbegin(); it ! </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Cubemaps.html b/man/Advanced-OpenGL/Cubemaps.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Cubemaps</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Cubemaps</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Cubemaps</h1> @@ -810,20 +556,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Depth-testing.html b/man/Advanced-OpenGL/Depth-testing.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Depth testing</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Depth testing</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Depth-testing</h1> <p> @@ -552,20 +297,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Face-culling.html b/man/Advanced-OpenGL/Face-culling.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Face culling</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Face culling</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Face-culling</h1> @@ -400,20 +146,3 @@ float vertices[] = { </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Framebuffers.html b/man/Advanced-OpenGL/Framebuffers.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Framebuffers</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Framebuffers</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Framebuffers</h1> @@ -831,20 +577,3 @@ float kernel[9] = float[]( </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Geometry-Shader.html b/man/Advanced-OpenGL/Geometry-Shader.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Geometry Shader</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Geometry Shader</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Geometry-Shader</h1> @@ -880,20 +626,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Instancing.html b/man/Advanced-OpenGL/Instancing.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Instancing</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Instancing</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Instancing</h1> @@ -695,20 +441,3 @@ for(unsigned int i = 0; i &lt; rock.meshes.size(); i++) </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Advanced-OpenGL/Stencil-testing.html b/man/Advanced-OpenGL/Stencil-testing.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Stencil testing</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Stencil testing</h1> <h1 id="content-url" style='display:none;'>Advanced-OpenGL/Stencil-testing</h1> @@ -562,20 +308,3 @@ DrawTwoScaledUpContainers(); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Code-repository.html b/man/Code-repository.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Code repository</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Code repository</h1> <h1 id="content-url" style='display:none;'>Code-repository</h1> <p> @@ -273,20 +18,3 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Getting-started/Camera.html b/man/Getting-started/Camera.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Camera</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Camera</h1> <h1 id="content-title">カメラ</h1> <h1 id="content-url" style='display:none;'>Getting-started/Camera</h1> diff --git a/man/Getting-started/Coordinate-Systems.html b/man/Getting-started/Coordinate-Systems.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Coordinate Systems</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Coordinate Systems</h1> <h1 id="content-title">座標系</h1> <h1 id="content-url" style='display:none;'>Getting-started/Coordinate-Systems</h1> diff --git a/man/Getting-started/Creating-a-window.html b/man/Getting-started/Creating-a-window.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Creating a window</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Creating a window</h1> <h1 id="content-title">ウィンドウの作成</h1> <h1 id="content-url" style='display:none;'>Getting-started/Creating-a-window</h1> diff --git a/man/Getting-started/Hello-Triangle.html b/man/Getting-started/Hello-Triangle.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Hello Triangle</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> -<div id="content"> <h1 id="content-title">Hello Triangle</h1> <h1 id="content-title">はじめての三角形</h1> <h1 id="content-url" style='display:none;'>Getting-started/Hello-Triangle</h1> diff --git a/man/Getting-started/Hello-Window.html b/man/Getting-started/Hello-Window.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Hello Window</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Hello Window</h1> <h1 id="content-title">はじめてのウィンドウ</h1> <h1 id="content-url" style='display:none;'>Getting-started/Hello-Window</h1> diff --git a/man/Getting-started/OpenGL.html b/man/Getting-started/OpenGL.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8" /> - <title>LearnOpenGL - OpenGL</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">OpenGL</h1> <h1 id="content-url" style='display:none;'>Getting-started/OpenGL</h1> <p>学習をはじめる前にOpenGLとは何かを確認しておきましょう。OpenGLとはグラフィックや画像を操作するための関数を多数提供するAPI(<def>アプリケーションプログラミングインターフェース</def>)だと考えられています。しかしOpenGLそのものはAPIではなく、<a href="http://www.khronos.org/" target="_blank">Khronos Group</a>により開発及びメンテナンスされている仕様です。</p> diff --git a/man/Getting-started/Review.html b/man/Getting-started/Review.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Review</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Review</h1> <h1 id="content-title">まとめ</h1> <h1 id="content-url" style='display:none;'>Getting-started/Review</h1> diff --git a/man/Getting-started/Shaders.html b/man/Getting-started/Shaders.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Shaders</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Shaders</h1> <h1 id="content-title">シェーダー</h1> <h1 id="content-url" style='display:none;'>Getting-started/Shaders</h1> diff --git a/man/Getting-started/Textures.html b/man/Getting-started/Textures.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Textures</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Textures</h1> <h1 id="content-title">テクスチャ</h1> <h1 id="content-url" style='display:none;'>Getting-started/Textures</h1> diff --git a/man/Getting-started/Transformations.html b/man/Getting-started/Transformations.html @@ -1,15 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Transformations</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="../static/style.css" /> - <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> - <script src="/static/functions.js"></script> -</head> -<body> - <div id="content"> <h1 id="content-title">Transformations</h1> <h1 id="content-title">変換</h1> <h1 id="content-url" style='display:none;'>Getting-started/Transformations</h1> diff --git a/man/Guest-Articles/2020/OIT/Introduction.html b/man/Guest-Articles/2020/OIT/Introduction.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Introduction</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Introduction</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2020/OIT/Introduction</h1> @@ -418,20 +164,3 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/2020/OIT/Weighted-Blended.html b/man/Guest-Articles/2020/OIT/Weighted-Blended.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Weighted Blended</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Weighted Blended</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2020/OIT/Weighted-Blended</h1> @@ -802,20 +548,3 @@ screenShader.use(); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/2020/Skeletal-Animation.html b/man/Guest-Articles/2020/Skeletal-Animation.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Skeletal Animation</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Skeletal Animation</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2020/Skeletal-Animation</h1> @@ -1139,20 +885,3 @@ and meshes are baked in single DAE(collada) file. You can find the full source c </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/2021/CSM.html b/man/Guest-Articles/2021/CSM.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - CSM</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">CSM</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2021/CSM</h1> @@ -719,20 +465,3 @@ return shadow; </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/2021/Scene/Frustum-Culling.html b/man/Guest-Articles/2021/Scene/Frustum-Culling.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Frustum Culling</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Frustum Culling</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2021/Scene/Frustum-Culling</h1> @@ -770,20 +516,3 @@ void drawSelfAndChild(const Frustum& frustum, Shader& ourShader, </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/2021/Scene/Scene-Graph.html b/man/Guest-Articles/2021/Scene/Scene-Graph.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Scene Graph</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Scene Graph</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/2021/Scene/Scene-Graph</h1> @@ -706,20 +452,3 @@ public: </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Guest-Articles/How-to-publish.html b/man/Guest-Articles/How-to-publish.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - How to publish</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">How to publish</h1> <h1 id="content-url" style='display:none;'>Guest-Articles/How-to-publish</h1> @@ -319,20 +65,3 @@ When you submit an article, please mention your full name, and a link to your we </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/In-Practice/2D-Game/Audio.html b/man/In-Practice/2D-Game/Audio.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Audio</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Audio</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Audio</h1> <p> @@ -403,4 +148,4 @@ void Game::Init() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Breakout.html b/man/In-Practice/2D-Game/Breakout.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Breakout</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Breakout</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Breakout</h1> <p> @@ -340,4 +85,4 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Collisions/Ball.html b/man/In-Practice/2D-Game/Collisions/Ball.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Ball</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Ball</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Ball</h1> <p> @@ -454,4 +199,4 @@ void Game::Render() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Collisions/Collision-detection.html b/man/In-Practice/2D-Game/Collisions/Collision-detection.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Collision detection</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Collision detection</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Collision-detection</h1> <p> @@ -491,4 +236,4 @@ bool CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Collisions/Collision-resolution.html b/man/In-Practice/2D-Game/Collisions/Collision-resolution.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Collision resolution</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Collision resolution</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Collision-resolution</h1> <p> @@ -567,4 +312,4 @@ void Game::Update(float dt) </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Final-thoughts.html b/man/In-Practice/2D-Game/Final-thoughts.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Final thoughts</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Final thoughts</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Final-thoughts</h1> <p> @@ -307,4 +52,4 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Levels.html b/man/In-Practice/2D-Game/Levels.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Levels</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Levels</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Levels</h1> <p> @@ -619,4 +364,4 @@ void Game::ProcessInput(float dt) </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Particles.html b/man/In-Practice/2D-Game/Particles.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Particles</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Particles</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Particles</h1> <p> @@ -562,4 +307,4 @@ void Game::Render() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Postprocessing.html b/man/In-Practice/2D-Game/Postprocessing.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Postprocessing</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Postprocessing</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Postprocessing</h1> <p> @@ -541,4 +286,4 @@ void Game::Update(float dt) </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Powerups.html b/man/In-Practice/2D-Game/Powerups.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Powerups</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Powerups</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Powerups</h1> <p> @@ -666,4 +411,4 @@ void Game::Render() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Render-text.html b/man/In-Practice/2D-Game/Render-text.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Render text</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Render text</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Render-text</h1> <p> @@ -680,4 +425,4 @@ void Game::ProcessInput(float dt) </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Rendering-Sprites.html b/man/In-Practice/2D-Game/Rendering-Sprites.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Rendering Sprites</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Rendering Sprites</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Rendering-Sprites</h1> <p> @@ -536,4 +281,4 @@ void Game::Render() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/2D-Game/Setting-up.html b/man/In-Practice/2D-Game/Setting-up.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Setting up</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Setting up</h1> <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Setting-up</h1> <p> @@ -417,4 +162,4 @@ ResourceManager::GetShader("test").Use(); </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/Debugging.html b/man/In-Practice/Debugging.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Debugging</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Debugging</h1> <h1 id="content-url" style='display:none;'>In-Practice/Debugging</h1> <p> @@ -820,4 +565,4 @@ int main() </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/In-Practice/Text-Rendering.html b/man/In-Practice/Text-Rendering.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Text Rendering</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Text Rendering</h1> <h1 id="content-url" style='display:none;'>In-Practice/Text-Rendering</h1> <p> @@ -690,4 +435,4 @@ RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0 </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/Introduction.html b/man/Introduction.html @@ -1,14 +1,3 @@ -<!DOCTYPE html> -<html lang="ja"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Introduction</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <link rel="stylesheet" href="static/style.css" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> -</head> -<body> <div id="content"> <h1 id="content-title">Introduction</h1> <h1 id="content-title">はじめに</h1> diff --git a/man/Lighting/Basic-Lighting.html b/man/Lighting/Basic-Lighting.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Basic Lighting</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Basic Lighting</h1> <h1 id="content-url" style='display:none;'>Lighting/Basic-Lighting</h1> <p> @@ -645,20 +390,3 @@ FragColor = vec4(result, 1.0); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Colors.html b/man/Lighting/Colors.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Colors</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Colors</h1> <h1 id="content-url" style='display:none;'>Lighting/Colors</h1> <p> @@ -475,20 +220,3 @@ lightCubeShader.use(); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Light-casters.html b/man/Lighting/Light-casters.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Light casters</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Light casters</h1> <h1 id="content-url" style='display:none;'>Lighting/Light-casters</h1> <p> @@ -802,20 +547,3 @@ specular *= intensity; </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Lighting-maps.html b/man/Lighting/Lighting-maps.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Lighting maps</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Lighting maps</h1> <h1 id="content-url" style='display:none;'>Lighting/Lighting-maps</h1> <p> @@ -465,20 +210,3 @@ FragColor = vec4(ambient + diffuse + specular, 1.0); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Materials.html b/man/Lighting/Materials.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Materials</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Materials</h1> <h1 id="content-url" style='display:none;'>Lighting/Materials</h1> <p> @@ -474,20 +219,3 @@ lightingShader.setVec3("light.diffuse", diffuseColor); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Multiple-lights.html b/man/Lighting/Multiple-lights.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Multiple lights</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Multiple lights</h1> <h1 id="content-url" style='display:none;'>Lighting/Multiple-lights</h1> <p> @@ -522,20 +267,3 @@ glm::vec3 pointLightPositions[] = { </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Lighting/Review.html b/man/Lighting/Review.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Review</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Review</h1> <h1 id="content-url" style='display:none;'>Lighting/Review</h1> <p> @@ -295,20 +40,3 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Model-Loading/Assimp.html b/man/Model-Loading/Assimp.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Assimp</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Assimp</h1> <h1 id="content-url" style='display:none;'>Model-Loading/Assimp</h1> @@ -344,20 +90,3 @@ manually in cmake. </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Model-Loading/Mesh.html b/man/Model-Loading/Mesh.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Mesh</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Mesh</h1> <h1 id="content-url" style='display:none;'>Model-Loading/Mesh</h1> @@ -494,20 +240,3 @@ void Draw(Shader &shader) </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Model-Loading/Model.html b/man/Model-Loading/Model.html @@ -1,257 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Model</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - <div id="content"> <h1 id="content-title">Model</h1> <h1 id="content-url" style='display:none;'>Model-Loading/Model</h1> @@ -680,20 +426,3 @@ vector&lt;Texture&gt; loadMaterialTextures(aiMaterial *mat, aiTextureType type, </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/PBR/IBL/Diffuse-irradiance.html b/man/PBR/IBL/Diffuse-irradiance.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Diffuse irradiance</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Diffuse irradiance</h1> <h1 id="content-url" style='display:none;'>PBR/IBL/Diffuse-irradiance</h1> <p> @@ -925,20 +670,3 @@ vec3 ambient = (kD * diffuse) * ao; </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/PBR/IBL/Specular-IBL.html b/man/PBR/IBL/Specular-IBL.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Specular IBL</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Specular IBL</h1> <h1 id="content-url" style='display:none;'>PBR/IBL/Specular-IBL</h1> <p> @@ -1107,20 +852,3 @@ vec3 ambient = (kD * diffuse + specular) * ao; </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/PBR/Lighting.html b/man/PBR/Lighting.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Lighting</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Lighting</h1> <h1 id="content-url" style='display:none;'>PBR/Lighting</h1> <p> @@ -677,20 +422,3 @@ void main() </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/PBR/Theory.html b/man/PBR/Theory.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Theory</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Theory</h1> <h1 id="content-url" style='display:none;'>PBR/Theory</h1> <p> @@ -858,20 +603,3 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0) </div> - <div id="hover"> - HI - </div> - <!-- 728x90/320x50 sticky footer --> -<div id="waldo-tag-6196"></div> - - <div id="disqus_thread"></div> - - - - -</div> <!-- container div --> - - -</div> <!-- super container div --> -</body> -</html> -\ No newline at end of file diff --git a/man/Translations.html b/man/Translations.html @@ -1,258 +1,3 @@ - - -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>LearnOpenGL - Translations</title> <!--<title>Learn OpenGL, extensive tutorial resource for learning Modern OpenGL</title>--> - <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> - <meta name="description" content="Learn OpenGL . com provides good and clear modern 3.3+ OpenGL tutorials with clear examples. A great resource to learn modern OpenGL aimed at beginners."> - <meta name="fragment" content="!"> - <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', 'UA-51879160-1', 'learnopengl.com'); - ga('send', 'pageview'); - - </script> - <!--<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>--> - <script> - (adsbygoogle = window.adsbygoogle || []).push({ - google_ad_client: "ca-pub-7855791439695850", - enable_page_level_ads: true - }); - </script> - <script async='async' src='https://www.googletagservices.com/tag/js/gpt.js'></script> - <script> - var googletag = googletag || {}; - googletag.cmd = googletag.cmd || []; - </script> - <script> - googletag.cmd.push(function() { - googletag.defineSlot('/8491498/learnopengl_video', [300, 225], 'div-gpt-ad-1540574378241-0').addService(googletag.pubads()); - googletag.pubads().enableSingleRequest(); - googletag.pubads().collapseEmptyDivs(); - googletag.enableServices(); - }); - </script> - <script type="text/javascript" src="https://d31vxm9ubutrmw.cloudfront.net/static/js/1681.js"></script> - <script src="/js/jquery-1.11.0.min.js"></script> - <script src="/js/hoverintent.js"></script> - <link rel="stylesheet" type="text/css" href="/layout.css"> - <link rel="stylesheet" type="text/css" href="/js/styles/obsidian.css"> - <script src="/js/highlight.pack.js"></script> - <script src="/js/functions.js"></script> - <script type="text/javascript" src="/js/mathjax/MathJax.js?config=TeX-AMS_HTML"></script> - <script> - // Has to be loaded last due to content bug - MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS" } } - }); - </script> - <script>hljs.initHighlightingOnLoad();</script> - <script> - $(document).ready(function() { - // check if user visited from the old # based urls, re-direct to ?p= form - if(window.location.hash) - { - var name = window.location.hash.substring(2); - // name = name.replace(/-/g," "); - var index = name.indexOf('#'); // Remove any hash fragments from the url (Disquss adds hash fragments for comments, but results in 404 pages) - if(index >= 0) - name = name.substring(0, index); - - window.location.href = "https://learnopengl.com/" + name; - } else { - // Check if data has been succesfully loaded, if so: change title bar as ajax hash fragment - var title = $('#content-url').text(); - - // Refresh syntax highlighting - // $('pre').each(function(i, e) {hljs.highlightBlock(e)}); - - // Reset DISQUS - // if(title == '/dev/') - // title = ''; - // alert('hoi'); - - // Adjust ads for correct bottom positioning based on content size - window.setTimeout(function() { - AdPositioning(); - }, 3000); - - - // set API resets after time-out (once content is properly loaded) - window.setTimeout(function() { - MathJax.Hub.Queue(["Typeset",MathJax.Hub]); - MathJax.Hub.Queue(["resetEquationNumbers", MathJax.InputJax.TeX]); - - var page_url = title == "" ? "http://www.learnopengl.com/" : "http://www.learnopengl.com/" + title; - if(typeof DISQUS !== 'undefined') { - DISQUS.reset({ - reload: true, - config: function () { - this.page.identifier = title; - this.page.url = page_url; - } - }); - $('#disqus_thread').show(); - } - // Refresh callbacks on <function> tags - SetFunctionTagCallbacks(); - }, 1000); - - // Zet ook de juiste button op 'selected' - $('#nav li span, #nav li a').removeClass('selected'); - if(title != '') - { - $('#nav li[id=\'' + title + '\']').children('span, a').addClass('selected'); - } - // En open menu waar nodig - var parents = $('#nav span.selected, #nav a.selected').parents('li').children('span.closed, a.closed'); - var index = 0; - for(index = parents.length - 1; index >= 0; index--) - { - - var id = $(parents[index]).attr("id").replace( /^\D+/g, ''); - MenuClick(id, false); - } - - } - }); - // var initialized = false; - // window.onpopstate = function() { - // if(initialized) - // LoadPage(); - // else - // initialized = true; - // }; - - // Set up DISQUS - // $(document).ready(function() { - var disqus_shortname = 'learnopengl'; - (function() { - var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); - })(); - // }); - </script> -</head> -<body> -<a href="https://learnopengl.com"> -<div id="header"> -</div> -</a> - -<div id="supercontainer"> - <!-- 728x90/320x50 --> - <div id="header_ad"> - <div id="waldo-tag-6194"></div> - </div> - <div id="rightad_container"> - <div id="rightad"> - <!-- /8491498/learnopengl_video --> - <!--<div id='div-gpt-ad-1540574378241-0' style='height:225px; width:300px;'> - <script> - googletag.cmd.push(function() { googletag.display('div-gpt-ad-1540574378241-0'); }); - </script> - </div> - <br/>--> - - <div id="waldo-tag-1715"></div> - </div> - - <div id="admessage"> - If you're running AdBlock, please consider whitelisting this site if you'd like to support LearnOpenGL; and no worries, I won't be mad if you don't :) - <!--<br/><br/> - Also, check out this little local multiplayer-only game I've made: <a href="https://store.steampowered.com/app/983590/Tank_Blazers/" target="_blank">Tank Blazers</a>. - <br/> - <a href="https://store.steampowered.com/app/983590/Tank_Blazers" target="_blank"><img src="/img/tank_blazers.jpg" style="width:278px; margin-top: 9px; margin-left: -3px;"/></a>--> - </div> - - <div id="rightonethirdad"> - <div id="waldo-tag-2246"></div> - </div> - - <div id="rightbottomad"> - <div id="waldo-tag-2247"></div> - </div> - </div> - <div id="container"> - <div id="loading"></div> -<script> -$(document).ready(function() { -$('#menu-item4').mousedown(function() { MenuClick(4, true) }); -$('#menu-item48').mousedown(function() { MenuClick(48, true) }); -$('#menu-item56').mousedown(function() { MenuClick(56, true) }); -$('#menu-item63').mousedown(function() { MenuClick(63, true) }); -$('#menu-item100').mousedown(function() { MenuClick(100, true) }); -$('#menu-item102').mousedown(function() { MenuClick(102, true) }); -$('#menu-item113').mousedown(function() { MenuClick(113, true) }); -$('#menu-item116').mousedown(function() { MenuClick(116, true) }); -$('#menu-item78').mousedown(function() { MenuClick(78, true) }); -$('#menu-item81').mousedown(function() { MenuClick(81, true) }); -$('#menu-item85').mousedown(function() { MenuClick(85, true) }); -$('#menu-item125').mousedown(function() { MenuClick(125, true) }); -$('#menu-item128').mousedown(function() { MenuClick(128, true) }); -$('#menu-item129').mousedown(function() { MenuClick(129, true) }); -$('#menu-item133').mousedown(function() { MenuClick(133, true) }); -$('#menu-item134').mousedown(function() { MenuClick(134, true) }); -}); -</script> - <div id="nav"> - <div id="social"> - <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank"> - <img src="/img/github.png" class="social_ico"> - </a> - <!-- <a href="https://www.facebook.com/Learnopengl-2199631333595544/" target="_blank"> - <img src="/img/facebook.png" class="social_ico"> - </a>--> - <a href="https://twitter.com/JoeyDeVriez" target="_blank"> - <img src="/img/twitter.png" class="social_ico"> - </a> - - </div> - <img src='img/nav-button_bottom-arrow.png' style='display: none'><ol><li id='Introduction'><a id="menu-item1" href="https://learnopengl.com/Introduction">Introduction </a></li><li id='Getting-started'><span id="menu-item4" class="closed">Getting started </span><ol id="menu-items-of4" style="display:none;"><li id='Getting-started/OpenGL'><a id="menu-item49" href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a></li><li id='Getting-started/Creating-a-window'><a id="menu-item5" href="https://learnopengl.com/Getting-started/Creating-a-window">Creating a window </a></li><li id='Getting-started/Hello-Window'><a id="menu-item6" href="https://learnopengl.com/Getting-started/Hello-Window">Hello Window </a></li><li id='Getting-started/Hello-Triangle'><a id="menu-item38" href="https://learnopengl.com/Getting-started/Hello-Triangle">Hello Triangle </a></li><li id='Getting-started/Shaders'><a id="menu-item39" href="https://learnopengl.com/Getting-started/Shaders">Shaders </a></li><li id='Getting-started/Textures'><a id="menu-item40" href="https://learnopengl.com/Getting-started/Textures">Textures </a></li><li id='Getting-started/Transformations'><a id="menu-item43" href="https://learnopengl.com/Getting-started/Transformations">Transformations </a></li><li id='Getting-started/Coordinate-Systems'><a id="menu-item44" href="https://learnopengl.com/Getting-started/Coordinate-Systems">Coordinate Systems </a></li><li id='Getting-started/Camera'><a id="menu-item47" href="https://learnopengl.com/Getting-started/Camera">Camera </a></li><li id='Getting-started/Review'><a id="menu-item50" href="https://learnopengl.com/Getting-started/Review">Review </a></li></ol></li><li id='Lighting'><span id="menu-item48" class="closed">Lighting </span><ol id="menu-items-of48" style="display:none;"><li id='Lighting/Colors'><a id="menu-item51" href="https://learnopengl.com/Lighting/Colors">Colors </a></li><li id='Lighting/Basic-Lighting'><a id="menu-item52" href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a></li><li id='Lighting/Materials'><a id="menu-item53" href="https://learnopengl.com/Lighting/Materials">Materials </a></li><li id='Lighting/Lighting-maps'><a id="menu-item54" href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a></li><li id='Lighting/Light-casters'><a id="menu-item55" href="https://learnopengl.com/Lighting/Light-casters">Light casters </a></li><li id='Lighting/Multiple-lights'><a id="menu-item58" href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a></li><li id='Lighting/Review'><a id="menu-item57" href="https://learnopengl.com/Lighting/Review">Review </a></li></ol></li><li id='Model-Loading'><span id="menu-item56" class="closed">Model Loading </span><ol id="menu-items-of56" style="display:none;"><li id='Model-Loading/Assimp'><a id="menu-item59" href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a></li><li id='Model-Loading/Mesh'><a id="menu-item60" href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a></li><li id='Model-Loading/Model'><a id="menu-item61" href="https://learnopengl.com/Model-Loading/Model">Model </a></li></ol></li><li id='Advanced-OpenGL'><span id="menu-item63" class="closed">Advanced OpenGL </span><ol id="menu-items-of63" style="display:none;"><li id='Advanced-OpenGL/Depth-testing'><a id="menu-item72" href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a></li><li id='Advanced-OpenGL/Stencil-testing'><a id="menu-item73" href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a></li><li id='Advanced-OpenGL/Blending'><a id="menu-item74" href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a></li><li id='Advanced-OpenGL/Face-culling'><a id="menu-item77" href="https://learnopengl.com/Advanced-OpenGL/Face-culling">Face culling </a></li><li id='Advanced-OpenGL/Framebuffers'><a id="menu-item65" href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a></li><li id='Advanced-OpenGL/Cubemaps'><a id="menu-item66" href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a></li><li id='Advanced-OpenGL/Advanced-Data'><a id="menu-item69" href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a></li><li id='Advanced-OpenGL/Advanced-GLSL'><a id="menu-item67" href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a></li><li id='Advanced-OpenGL/Geometry-Shader'><a id="menu-item68" href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a></li><li id='Advanced-OpenGL/Instancing'><a id="menu-item70" href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a></li><li id='Advanced-OpenGL/Anti-Aliasing'><a id="menu-item75" href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a></li></ol></li><li id='Advanced-Lighting'><span id="menu-item100" class="closed">Advanced Lighting </span><ol id="menu-items-of100" style="display:none;"><li id='Advanced-Lighting/Advanced-Lighting'><a id="menu-item101" href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a></li><li id='Advanced-Lighting/Gamma-Correction'><a id="menu-item110" href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a></li><li id='Advanced-Lighting/Shadows'><span id="menu-item102" class="closed">Shadows </span><ol id="menu-items-of102" style="display:none;"><li id='Advanced-Lighting/Shadows/Shadow-Mapping'><a id="menu-item103" href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a></li><li id='Advanced-Lighting/Shadows/Point-Shadows'><a id="menu-item104" href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a></li></ol></li><li id='Advanced-Lighting/Normal-Mapping'><a id="menu-item106" href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a></li><li id='Advanced-Lighting/Parallax-Mapping'><a id="menu-item107" href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a></li><li id='Advanced-Lighting/HDR'><a id="menu-item111" href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a></li><li id='Advanced-Lighting/Bloom'><a id="menu-item112" href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a></li><li id='Advanced-Lighting/Deferred-Shading'><a id="menu-item108" href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a></li><li id='Advanced-Lighting/SSAO'><a id="menu-item109" href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a></li></ol></li><li id='PBR'><span id="menu-item113" class="closed">PBR </span><ol id="menu-items-of113" style="display:none;"><li id='PBR/Theory'><a id="menu-item114" href="https://learnopengl.com/PBR/Theory">Theory </a></li><li id='PBR/Lighting'><a id="menu-item115" href="https://learnopengl.com/PBR/Lighting">Lighting </a></li><li id='PBR/IBL'><span id="menu-item116" class="closed">IBL </span><ol id="menu-items-of116" style="display:none;"><li id='PBR/IBL/Diffuse-irradiance'><a id="menu-item117" href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a></li><li id='PBR/IBL/Specular-IBL'><a id="menu-item118" href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a></li></ol></li></ol></li><li id='In-Practice'><span id="menu-item78" class="closed">In Practice </span><ol id="menu-items-of78" style="display:none;"><li id='In-Practice/Debugging'><a id="menu-item79" href="https://learnopengl.com/In-Practice/Debugging">Debugging </a></li><li id='In-Practice/Text-Rendering'><a id="menu-item80" href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a></li><li id='In-Practice/2D-Game'><span id="menu-item81" class="closed">2D Game </span><ol id="menu-items-of81" style="display:none;"><li id='In-Practice/2D-Game/Breakout'><a id="menu-item82" href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a></li><li id='In-Practice/2D-Game/Setting-up'><a id="menu-item88" href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a></li><li id='In-Practice/2D-Game/Rendering-Sprites'><a id="menu-item83" href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a></li><li id='In-Practice/2D-Game/Levels'><a id="menu-item84" href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a></li><li id='In-Practice/2D-Game/Collisions'><span id="menu-item85" class="closed">Collisions </span><ol id="menu-items-of85" style="display:none;"><li id='In-Practice/2D-Game/Collisions/Ball'><a id="menu-item95" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a></li><li id='In-Practice/2D-Game/Collisions/Collision-detection'><a id="menu-item96" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a></li><li id='In-Practice/2D-Game/Collisions/Collision-resolution'><a id="menu-item97" href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a></li></ol></li><li id='In-Practice/2D-Game/Particles'><a id="menu-item89" href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a></li><li id='In-Practice/2D-Game/Postprocessing'><a id="menu-item90" href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a></li><li id='In-Practice/2D-Game/Powerups'><a id="menu-item91" href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a></li><li id='In-Practice/2D-Game/Audio'><a id="menu-item94" href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a></li><li id='In-Practice/2D-Game/Render-text'><a id="menu-item92" href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a></li><li id='In-Practice/2D-Game/Final-thoughts'><a id="menu-item93" href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a></li></ol></li></ol></li><li id='Guest-Articles'><span id="menu-item125" class="closed">Guest Articles </span><ol id="menu-items-of125" style="display:none;"><li id='Guest-Articles/How-to-publish'><a id="menu-item126" href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a></li><li id='Guest-Articles/2020'><span id="menu-item128" class="closed">2020 </span><ol id="menu-items-of128" style="display:none;"><li id='Guest-Articles/2020/OIT'><span id="menu-item129" class="closed">OIT </span><ol id="menu-items-of129" style="display:none;"><li id='Guest-Articles/2020/OIT/Introduction'><a id="menu-item130" href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a></li><li id='Guest-Articles/2020/OIT/Weighted-Blended'><a id="menu-item132" href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a></li></ol></li><li id='Guest-Articles/2020/Skeletal-Animation'><a id="menu-item131" href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a></li></ol></li><li id='Guest-Articles/2021'><span id="menu-item133" class="closed">2021 </span><ol id="menu-items-of133" style="display:none;"><li id='Guest-Articles/2021/CSM'><a id="menu-item137" href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a></li><li id='Guest-Articles/2021/Scene'><span id="menu-item134" class="closed">Scene </span><ol id="menu-items-of134" style="display:none;"><li id='Guest-Articles/2021/Scene/Scene-Graph'><a id="menu-item135" href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a></li><li id='Guest-Articles/2021/Scene/Frustum-Culling'><a id="menu-item136" href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a></li></ol></li></ol></li></ol></li><li id='Code-repository'><a id="menu-item99" href="https://learnopengl.com/Code-repository">Code repository </a></li><li id='Translations'><a id="menu-item119" href="https://learnopengl.com/Translations">Translations </a></li><li id='About'><a id="menu-item2" href="https://learnopengl.com/About">About </a></li></ol> <div id="menu_book"> - <a href="https://geni.us/learnopengl" target="_blank"><img src="/book/below_menu.png" class="clean"/></a> - </div> - <div id="donate"> - <a href="https://www.paypal.me/learnopengl/" target="_blank"> - <div id="donate_img"></div> - <img style="display: none" src="/img/donate_button_hover.png"/> - <!--<img id="donate_img" src="img/patreon.png"/>--> - </a> - <!--<div id="alipay"> - <img style="width: 150px;" class="clean" src="/img/alipay_logo.png"/> - <img style="width: 150px; margin-top: 5px" src="/img/alipay.png"/> - </div>--> - </div> - <div class="btc"> - <h3>BTC</h3> - <p> - 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa - </p> - <img src="/img/btc_qr.png"/> - </div> - <div class="btc"> - <h3>ETH/ERC20</h3> - <p> - 0x1de59bd9e52521a46309474f8372531533bd7c43 - </p> - <img src="/img/erc20_qr.png"/> - </div> - <div id="ad"> - <!--<div id="waldo-tag-1684"></div>--> - </div> - - <div id="lefttwothirdad"> - <div id="waldo-tag-2245"></div> - </div> - </div> - - <div id="content"> <h1 id="content-title">Translations</h1> <h1 id="content-url" style='display:none;'>Translations</h1> <p> @@ -321,4 +66,4 @@ $('#menu-item134').mousedown(function() { MenuClick(134, true) }); </div> <!-- super container div --> </body> -</html> -\ No newline at end of file +</html> diff --git a/man/static/style.css b/man/static/style.css @@ -5,6 +5,18 @@ body { padding: 0px; background-image: url('img/header_repeat2.png'); background-repeat: repeat-x; + display: flex; + justify-content: space-around; +} + +nav { + width: 250px; + padding: 10px; +} + +main { + max-width: 1000px; + padding: 10px; } img.translation { diff --git a/pub/About.html b/pub/About.html @@ -0,0 +1,348 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">About</h1> +<h1 id="content-url" style='display:none;'>About</h1> +<p> + Hi, my name is <a href="http://joeydevries.com" target="_blank">Joey de Vries</a> and I'm the sole author of LearnOpenGL. As you have probably guessed, I'm a computer graphics enthusiast. +</p> + +<p> + Computer graphics have always been a strong interest of mine, either because I enjoyed the visuals of movie effects and video games, or simply liked moving pixels. It wasn't until joining a computer science program at Utrecht University that I started to take programming and computer graphics seriously. I developed a strong eagerness to learn computer graphics (specifically OpenGL) and video game development, honing these skills as a hobby alongside my studies; using a multitude of online tutorials similar to sites like this. However, I always felt that the available online tutorials regarding OpenGL were either incomplete, contained errors or try to be overly complicated for no good reason. Therefore, it's always been in my mind to set up an OpenGL tutorial resource one day that tries to overcome these hurdles. +</p> + +<p> + Around June 2014 I published this website and started generating a large batch of content for everyone to enjoy. I am continuously striving to create one of the best OpenGL resources while also making it easy-to-understand and fun to learn. This means I will try to add as much content as I see fit and frequently revisit old chapters to improve their content. By now I have over 5 years of professional industry experience, both in WebGL/OpenGL and as a AAA game engine developer. This experience translates in continuous improvements on Learn OpenGL as I fix mistakes, improve explanations, and learn better approaches to teaching. Let me know what you think of the site in the comments or by contacting me <a href="mailto:learnopengl.com@gmail.com">personally</a> and let's try to together make this the number one resource for learning modern OpenGL! +</p> + +<p> + All code samples, unless explicitly stated otherwise, are licensed under the terms of the CC BY-NC 4.0 license as published by Creative Commons, either version 4 of the License, or (at your option) any later version. You can find a human-readable format of the license <a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">here</a> and the full license <a href="https://creativecommons.org/licenses/by-nc/4.0/legalcode" target="_blank">here</a>. +</p> + +<p> + Similarly, all images (and videos) are licensed under the terms of the CC BY 4.0 license as published by Creative Commons, either version 4 of the License, or (at your option) any later version. You can find a human-readable format of the license <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">here</a> and the full license <a href="https://creativecommons.org/licenses/by/4.0/legalcode" target="_blank">here</a>. +</p> + +<p> + When attributing any of the licensed works, please include a copyright notice including a (hyper)link to the copyrighted work and a link to the specific license. Please also mention my full name (Joey de Vries), this website (or a link to the relevant article where applicable), and my personal twitter handle: <a href="https://twitter.com/JoeyDeVriez" target="_blank">https://twitter.com/JoeyDeVriez</a>. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Advanced-Lighting.html b/pub/Advanced-Lighting/Advanced-Lighting.html @@ -0,0 +1,438 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Advanced Lighting</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Advanced-Lighting</h1> +<p> + In the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">lighting</a> chapters we briefly introduced the Phong lighting model to bring a basic amount of realism into our scenes. The Phong model looks nice, but has a few nuances we'll focus on in this chapter. +</p> + +<h2>Blinn-Phong</h2> +<p> + Phong lighting is a great and very efficient approximation of lighting, but its specular reflections break down in certain conditions, specifically when the shininess property is low resulting in a large (rough) specular area. The image below shows what happens when we use a specular shininess exponent of <code>1.0</code> on a flat textured plane: +</p> + +<img src="/img/advanced-lighting/advanced_lighting_phong_limit.png" class="clean" alt="Result of Phong specular reflection with low exponent"/> + +<p> + You can see at the edges that the specular area is immediately cut off. The reason this happens is because the angle between the view and reflection vector doesn't go over 90 degrees. If the angle is larger than 90 degrees, the resulting dot product becomes negative and this results in a specular exponent of <code>0.0</code>. You're probably thinking this won't be a problem since we shouldn't get any light with angles higher than 90 degrees anyways, right? +</p> + +<p> + Wrong, this only applies to the diffuse component where an angle higher than 90 degrees between the normal and light source means the light source is below the lighted surface and thus the light's diffuse contribution should equal <code>0.0</code>. However, with specular lighting we're not measuring the angle between the light source and the normal, but between the view and reflection vector. Take a look at the following two images: +</p> + +<img src="/img/advanced-lighting/advanced_lighting_over_90.png" class="clean" alt="Image of Phong's reflection vectors being incorrect when larger than 90 degrees"/> + +<p> + Here the issue should become apparent. The left image shows Phong reflections as familiar, with \(\theta\) being less than 90 degrees. In the right image we can see that the angle \(\theta\) between the view and reflection vector is larger than 90 degrees which as a result nullifies the specular contribution. This generally isn't a problem since the view direction is far from the reflection direction, but if we use a low specular exponent the specular radius is large enough to have a contribution under these conditions. Since we're nullifying this contribution at angles larger than 90 degrees we get the artifact as seen in the first image. +</p> + +<p> + In 1977 the <def>Blinn-Phong</def> shading model was introduced by James F. Blinn as an extension to the Phong shading we've used so far. The Blinn-Phong model is largely similar, but approaches the specular model slightly different which as a result overcomes our problem. Instead of relying on a reflection vector we're using a so called <def>halfway vector</def> that is a unit vector exactly halfway between the view direction and the light direction. The closer this halfway vector aligns with the surface's normal vector, the higher the specular contribution. +</p> + +<img src="/img/advanced-lighting/advanced_lighting_halfway_vector.png" class="clean" alt="Illustration of Blinn-Phong's halfway vector"/> + +<p> + When the view direction is perfectly aligned with the (now imaginary) reflection vector, the halfway vector aligns perfectly with the normal vector. The closer the view direction is to the original reflection direction, the stronger the specular highlight. +</p> + +<p> + Here you can see that whatever direction the viewer looks from, the angle between the halfway vector and the surface normal never exceeds 90 degrees (unless the light is far below the surface of course). The results are slightly different from Phong reflections, but generally more visually plausible, especially with low specular exponents. The Blinn-Phong shading model is also the exact shading model used in the earlier fixed function pipeline of OpenGL. +</p> + +<p> + Getting the halfway vector is easy, we add the light's direction vector and view vector together and normalize the result: +</p> + +\[\bar{H} = \frac{\bar{L} + \bar{V}}{||\bar{L} + \bar{V}||}\] + +<p> + This translates to GLSL code as follows: +</p> + +<pre><code> +vec3 lightDir = normalize(lightPos - FragPos); +vec3 viewDir = normalize(viewPos - FragPos); +vec3 halfwayDir = normalize(lightDir + viewDir); +</code></pre> + +<p> + Then the actual calculation of the specular term becomes a clamped dot product between the surface normal and the halfway vector to get the cosine angle between them that we again raise to a specular shininess exponent: +</p> + +<pre><code> +float spec = pow(max(dot(normal, halfwayDir), 0.0), shininess); +vec3 specular = lightColor * spec; +</code></pre> + +<p> + And there is nothing more to Blinn-Phong than what we just described. The only difference between Blinn-Phong and Phong specular reflection is that we now measure the angle between the normal and halfway vector instead of the angle between the view and reflection vector. +</p> + +<p> + With the introduction of the halfway vector we should no longer have the specular cutoff issue of Phong shading. The image below shows the specular area of both methods with a specular exponent of <code>0.5</code>: +</p> + + +<img src="/img/advanced-lighting/advanced_lighting_comparrison.png" alt="Comparison between Phong and Blinn-Phong shading with a low exponent"/> + +<p> + Another subtle difference between Phong and Blinn-Phong shading is that the angle between the halfway vector and the surface normal is often shorter than the angle between the view and reflection vector. As a result, to get visuals similar to Phong shading the specular shininess exponent has to be set a bit higher. A general rule of thumb is to set it between 2 and 4 times the Phong shininess exponent. +</p> + +<p> + Below is a comparison between both specular reflection models with the Phong exponent set to <code>8.0</code> and the Blinn-Phong component set to <code>32.0</code>: +</p> + +<img src="/img/advanced-lighting/advanced_lighting_comparrison2.png" alt="Comparison between Phong and Blinn-Phong shading with normal exponents"/> + +<p> + You can see that the Blinn-Phong specular exponent is bit sharper compared to Phong. It usually requires a bit of tweaking to get similar results as to what you previously had with Phong shading. It's worth it though as Blinn-Phong shading is generally more realistic compared to default Phong shading. +</p> + +<p> + Here we used a simple fragment shader that switches between regular Phong reflections and Blinn-Phong reflections: +</p> + +<pre><code> +void main() +{ + [...] + float spec = 0.0; + if(blinn) + { + vec3 halfwayDir = normalize(lightDir + viewDir); + spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0); + } + else + { + vec3 reflectDir = reflect(-lightDir, normal); + spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0); + } +</code></pre> + +<p> + You can find the source code for the simple demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/1.advanced_lighting/advanced_lighting.cpp" target="_blank">here</a>. By pressing the <code>b</code> key, the demo switches from Phong to Blinn-Phong lighting and vica versa. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Bloom.html b/pub/Advanced-Lighting/Bloom.html @@ -0,0 +1,661 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Bloom</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Bloom</h1> +<p> + Bright light sources and brightly lit regions are often difficult to convey to the viewer as the intensity range of a monitor is limited. One way to distinguish bright light sources on a monitor is by making them glow; the light then <em>bleeds</em> around the light source. This effectively gives the viewer the illusion these light sources or bright regions are intensely bright. +</p> + +<p> + This light bleeding, or glow effect, is achieved with a post-processing effect called <def>Bloom</def>. Bloom gives all brightly lit regions of a scene a glow-like effect. An example of a scene with and without glow can be seen below (image courtesy of Epic Games): +</p> + +<img src="/img/advanced-lighting/bloom_example.png" "Example of Bloom or glow in OpenGL tutorial"/> + +<p> + Bloom gives noticeable visual cues about the brightness of objects. When done in a subtle fashion (which some games drastically fail to do) Bloom significantly boosts the lighting of your scene and allows for a large range of dramatic effects. +</p> + +<p> + Bloom works best in combination with <a href="https://learnopengl.com/Advanced-Lighting/HDR" target="_blank">HDR</a> rendering. A common misconception is that HDR is the same as Bloom as many people use the terms interchangeably. They are however completely different techniques used for different purposes. It is possible to implement Bloom with default 8-bit precision framebuffers, just as it is possible to use HDR without the Bloom effect. It is simply that HDR makes Bloom more effective to implement (as we'll later see). +</p> + +<p> + To implement Bloom, we render a lit scene as usual and extract both the scene's HDR color buffer and an image of the scene with only its bright regions visible. This extracted brightness image is then blurred and the result added on top of the original HDR scene image. +</p> + +<p> + Let's illustrate this process in a step by step fashion. We render a scene filled with 4 bright light sources, visualized as colored cubes. The colored light cubes have a brightness values between <code>1.5</code> and <code>15.0</code>. If we were to render this to an HDR color buffer the scene looks as follows: +</p> + + <img src="/img/advanced-lighting/bloom_scene.png" class="clean" alt="Image of a HDR scene where we need to add the bloom or glow effect in OpenGL"/> + +<p> + We take this HDR color buffer texture and extract all the fragments that exceed a certain brightness. This gives us an image that only show the bright colored regions as their fragment intensities exceeded a certain threshold: +</p> + + <img src="/img/advanced-lighting/bloom_extracted.png" class="clean" alt="Bright regions extracted of a scene for the bloom or glow post-processing effect in OpenGL"/> + +<p> + We then take this thresholded brightness texture and blur the result. The strength of the bloom effect is largely determined by the range and strength of the blur filter used. +</p> + + <img src="/img/advanced-lighting/bloom_blurred.png" class="clean" alt="Bright regions extracted for glow or bloom effect are blurred in OpenGL"/> + +<p> + The resulting blurred texture is what we use to get the glow or light-bleeding effect. This blurred texture is added on top of the original HDR scene texture. Because the bright regions are extended in both width and height due to the blur filter, the bright regions of the scene appear to glow or <em>bleed</em> light. +</p> + +<img src="/img/advanced-lighting/bloom_small.png" class="clean" alt="Example of the Bloom or Glow post-processing effect in OpenGL with HDR"/> + +<p> + Bloom by itself isn't a complicated technique, but difficult to get exactly right. Most of its visual quality is determined by the quality and type of blur filter used for blurring the extracted brightness regions. Simply tweaking the blur filter can drastically change the quality of the Bloom effect. +</p> + +<p> + Following these steps gives us the Bloom post-processing effect. The next image briefly summarizes the required steps for implementing Bloom: +</p> + + <img src="/img/advanced-lighting/bloom_steps.png" class="clean" alt="Steps required for implementing the bloom or glow post-processing effect in OpenGL"/> + +<p> + The first step requires us to extract all the bright colors of a scene based on some threshold. Let's first delve into that. +</p> + +<h2>Extracting bright color</h2> +<p> + The first step requires us to extract two images from a rendered scene. We could render the scene twice, both rendering to a different framebuffer with different shaders, but we can also use a neat little trick called <def>Multiple Render Targets (MRT)</def> that allows us to specify more than one fragment shader output; this gives us the option to extract the first two images in a single render pass. By specifying a layout location specifier before a fragment shader's output we can control to which color buffer a fragment shader writes to: +</p> + +<pre><code> +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 BrightColor; +</code></pre> + +<p> + This only works if we actually have multiple buffers to write to. As a requirement for using multiple fragment shader outputs we need multiple color buffers attached to the currently bound framebuffer object. You may remember from the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> chapter that we can specify a color attachment number when linking a texture as a framebuffer's color buffer. Up until now we've always used <var>GL_COLOR_ATTACHMENT0</var>, but by also using <var>GL_COLOR_ATTACHMENT1</var> we can have two color buffers attached to a framebuffer object: +</p> + +<pre><code> +// set up floating point framebuffer to render scene to +unsigned int hdrFBO; +<function id='76'>glGenFramebuffers</function>(1, &hdrFBO); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, hdrFBO); +unsigned int colorBuffers[2]; +<function id='50'>glGenTextures</function>(2, colorBuffers); +for (unsigned int i = 0; i &lt; 2; i++) +{ + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, colorBuffers[i]); + <function id='52'>glTexImage2D</function>( + GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL + ); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // attach texture to framebuffer + <function id='81'>glFramebufferTexture2D</function>( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0 + ); +} +</code></pre> + +<p> + We do have to explicitly tell OpenGL we're rendering to multiple colorbuffers via <fun>glDrawBuffers</fun>. OpenGL, by default, only renders to a framebuffer's first color attachment, ignoring all others. We can do this by passing an array of color attachment enums that we'd like to render to in subsequent operations: +</p> + +<pre><code> +unsigned int attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; +glDrawBuffers(2, attachments); +</code></pre> + +<p> + When rendering into this framebuffer, whenever a fragment shader uses the layout location specifier, the respective color buffer is used to render the fragment to. This is great as this saves us an extra render pass for extracting bright regions as we can now directly extract them from the to-be-rendered fragment: +</p> + +<pre><code> +#version 330 core +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 BrightColor; + +[...] + +void main() +{ + [...] // first do normal lighting calculations and output results + FragColor = vec4(lighting, 1.0); + // check whether fragment output is higher than threshold, if so output as brightness color + float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + if(brightness &gt; 1.0) + BrightColor = vec4(FragColor.rgb, 1.0); + else + BrightColor = vec4(0.0, 0.0, 0.0, 1.0); +} +</code></pre> + +<p> + Here we first calculate lighting as normal and pass it to the first fragment shader's output variable <var>FragColor</var>. Then we use what is currently stored in <var>FragColor</var> to determine if its brightness exceeds a certain threshold. We calculate the brightness of a fragment by properly transforming it to grayscale first (by taking the dot product of both vectors we effectively multiply each individual component of both vectors and add the results together). If the brightness exceeds a certain threshold, we output the color to the second color buffer. We do the same for the light cubes. +</p> + +<p> + This also shows why Bloom works incredibly well with HDR rendering. Because we render in high dynamic range, color values can exceed <code>1.0</code> which allows us to specify a brightness threshold outside the default range, giving us much more control over what is considered bright. Without HDR we'd have to set the threshold lower than <code>1.0</code>, which is still possible, but regions are much quicker considered bright. This sometimes leads to the glow effect becoming too dominant (think of white glowing snow for example). +</p> + +<p> + With these two color buffers we have an image of the scene as normal, and an image of the extracted bright regions; all generated in a single render pass. +</p> + + <img src="/img/advanced-lighting/bloom_attachments.png" alt="Image of two colorbuffers obtained from a single render pass with multiple color attachments for the bloom or glow effect in OpenGL"/> + +<p> + With an image of the extracted bright regions we now need to blur the image. We can do this with a simple box filter as we've done in the post-processing section of the framebufers chapter, but we'd rather use a more advanced (and better-looking) blur filter called <def>Gaussian blur</def>. +</p> + +<h2>Gaussian blur</h2> +<p> + In the post-processing chapter's blur we took the average of all surrounding pixels of an image. While it does give us an easy blur, it doesn't give the best results. A Gaussian blur is based on the Gaussian curve which is commonly described as a <em>bell-shaped curve</em> giving high values close to its center that gradually wear off over distance. The Gaussian curve can be mathematically represented in different forms, but generally has the following shape: +</p> + + <img src="/img/advanced-lighting/bloom_gaussian.png" class="clean" alt="Image of a Gaussian Curve used for blurring a bloom or glow image in OpenGL"/> + +<p> + As the Gaussian curve has a larger area close to its center, using its values as weights to blur an image give more natural results as samples close by have a higher precedence. If we for instance sample a 32x32 box around a fragment, we use progressively smaller weights the larger the distance to the fragment; this gives a better and more realistic blur which is known as a <def>Gaussian blur</def>. +</p> + +<p> + To implement a Gaussian blur filter we'd need a two-dimensional box of weights that we can obtain from a 2 dimensional Gaussian curve equation. The problem with this approach however is that it quickly becomes extremely heavy on performance. Take a blur kernel of 32 by 32 for example, this would require us to sample a texture a total of 1024 times for each fragment! +</p> + +<p> + Luckily for us, the Gaussian equation has a very neat property that allows us to separate the two-dimensional equation into two smaller one-dimensional equations: one that describes the horizontal weights and the other that describes the vertical weights. We'd then first do a horizontal blur with the horizontal weights on the scene texture, and then on the resulting texture do a vertical blur. Due to this property the results are exactly the same, but this time saving us an incredible amount of performance as we'd now only have to do 32 + 32 samples compared to 1024! This is known as <def>two-pass Gaussian blur</def>. + </p> + + <img src="/img/advanced-lighting/bloom_gaussian_two_pass.png" class="clean" alt="Image of two-pass Gaussian blur with the same results as normal gaussian blur, but now saving a lot of performance in OpenGL"/> + +<p> + This does mean we need to blur an image at least two times and this works best with the use of framebuffer objects. Specifically for the two-pass Gaussian blur we're going to implement <em>ping-pong</em> framebuffers. That is a pair of framebuffers where we render and swap, a given number of times, the other framebuffer's color buffer into the current framebuffer's color buffer with an alternating shader effect. We basically continuously switch the framebuffer to render to and the texture to draw with. This allows us to first blur the scene's texture in the first framebuffer, then blur the first framebuffer's color buffer into the second framebuffer, and then the second framebuffer's color buffer into the first, and so on. +</p> + +<p> + Before we delve into the framebuffers let's first discuss the Gaussian blur's fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D image; + +uniform bool horizontal; +uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); + +void main() +{ + vec2 tex_offset = 1.0 / textureSize(image, 0); // gets size of single texel + vec3 result = texture(image, TexCoords).rgb * weight[0]; // current fragment's contribution + if(horizontal) + { + for(int i = 1; i &lt; 5; ++i) + { + result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; + result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i]; + } + } + else + { + for(int i = 1; i &lt; 5; ++i) + { + result += texture(image, TexCoords + vec2(0.0, tex_offset.y * i)).rgb * weight[i]; + result += texture(image, TexCoords - vec2(0.0, tex_offset.y * i)).rgb * weight[i]; + } + } + FragColor = vec4(result, 1.0); +} +</code></pre> + +<p> + Here we take a relatively small sample of Gaussian weights that we each use to assign a specific weight to the horizontal or vertical samples around the current fragment. You can see that we split the blur filter into a horizontal and vertical section based on whatever value we set the <var>horizontal</var> uniform. We base the offset distance on the exact size of a texel obtained by the division of <code>1.0</code> over the size of the texture (a <code>vec2</code> from <fun>textureSize</fun>). +</p> + +<p> + For blurring an image we create two basic framebuffers, each with only a color buffer texture: +</p> + +<pre><code> +unsigned int pingpongFBO[2]; +unsigned int pingpongBuffer[2]; +<function id='76'>glGenFramebuffers</function>(2, pingpongFBO); +<function id='50'>glGenTextures</function>(2, pingpongBuffer); +for (unsigned int i = 0; i &lt; 2; i++) +{ + <function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, pingpongFBO[i]); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, pingpongBuffer[i]); + <function id='52'>glTexImage2D</function>( + GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL + ); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + <function id='81'>glFramebufferTexture2D</function>( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongBuffer[i], 0 + ); +} +</code></pre> + +<p> + Then after we've obtained an HDR texture and an extracted brightness texture, we first fill one of the ping-pong framebuffers with the brightness texture and then blur the image 10 times (5 times horizontally and 5 times vertically): +</p> + +<pre><code> +bool horizontal = true, first_iteration = true; +int amount = 10; +shaderBlur.use(); +for (unsigned int i = 0; i &lt; amount; i++) +{ + <function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, pingpongFBO[horizontal]); + shaderBlur.setInt("horizontal", horizontal); + <function id='48'>glBindTexture</function>( + GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongBuffers[!horizontal] + ); + RenderQuad(); + horizontal = !horizontal; + if (first_iteration) + first_iteration = false; +} +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + Each iteration we bind one of the two framebuffers based on whether we want to blur horizontally or vertically and bind the other framebuffer's color buffer as the texture to blur. The first iteration we specifically bind the texture we'd like to blur (<var>brightnessTexture</var>) as both color buffers would else end up empty. By repeating this process 10 times, the brightness image ends up with a complete Gaussian blur that was repeated 5 times. This construct allows us to blur any image as often as we'd like; the more Gaussian blur iterations, the stronger the blur. +</p> + +<p> + By blurring the extracted brightness texture 5 times, we get a properly blurred image of all bright regions of a scene. +</p> + + <img src="/img/advanced-lighting/bloom_blurred_large.png" class="clean" alt="Blurred image using Gaussian Blur of extracted brightness regions for the glow or bloom effect in OpenGL"/> + +<p> + The last step to complete the Bloom effect is to combine this blurred brightness texture with the original scene's HDR texture. +</p> + +<h2>Blending both textures</h2> +<p> + With the scene's HDR texture and a blurred brightness texture of the scene we only need to combine the two to achieve the infamous Bloom or glow effect. In the final fragment shader (largely similar to the one we used in the <a href="https://learnopengl.com/Advanced-Lighting/HDR" target="_blank">HDR</a> chapter) we additively blend both textures: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D scene; +uniform sampler2D bloomBlur; +uniform float exposure; + +void main() +{ + const float gamma = 2.2; + vec3 hdrColor = texture(scene, TexCoords).rgb; + vec3 bloomColor = texture(bloomBlur, TexCoords).rgb; + hdrColor += bloomColor; // additive blending + // tone mapping + vec3 result = vec3(1.0) - exp(-hdrColor * exposure); + // also gamma correct while we're at it + result = pow(result, vec3(1.0 / gamma)); + FragColor = vec4(result, 1.0); +} +</code></pre> + +<p> + Interesting to note here is that we add the Bloom effect before we apply tone mapping. This way, the added brightness of bloom is also softly transformed to LDR range with better relative lighting as a result. +</p> + +<p> + With both textures added together, all bright areas of our scene now get a proper glow effect: +</p> + + <img src="/img/advanced-lighting/bloom.png" class="clean" alt="Example of the Bloom or Glow post-processing effect in OpenGL with HDR"/> + +<p> + The colored cubes now appear much more bright and give a better illusion as light emitting objects. This is a relatively simple scene so the Bloom effect isn't too impressive here, but in well lit scenes it can make a significant difference when properly configured. You can find the source code of this simple demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/7.bloom/bloom.cpp" target="_blank">here</a>. +</p> + +<p> + For this chapter we used a relatively simple Gaussian blur filter where we only take 5 samples in each direction. By taking more samples along a larger radius or repeating the blur filter an extra number of times we can improve the blur effect. As the quality of the blur directly correlates to the quality of the Bloom effect, improving the blur step can make a significant improvement. Some of these improvements combine blur filters with varying sized blur kernels or use multiple Gaussian curves to selectively combine weights. The additional resources from Kalogirou and Epic Games discuss how to significantly improve the Bloom effect by improving the Gaussian blur. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/" target="_blank">Efficient Gaussian Blur with linear sampling</a>: descirbes the Gaussian blur very well and how to improve its performance using OpenGL's bilinear texture sampling.</li> + <li><a href="https://udk-legacy.unrealengine.com/udk/Three/Bloom.html" target="_blank">Bloom Post Process Effect</a>: article from Epic Games about improving the Bloom effect by combining multiple Gaussian curves for its weights.</li> + <li><a href="http://kalogirou.net/2006/05/20/how-to-do-good-bloom-for-hdr-rendering/" target="_blank">How to do good Bloom for HDR rendering</a>: Article from Kalogirou that describes how to improve the Bloom effect using a better Gaussian blur method.</li> + </ul> + + + + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Deferred-Shading.html b/pub/Advanced-Lighting/Deferred-Shading.html @@ -0,0 +1,844 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Deferred Shading</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Deferred-Shading</h1> +<p> + The way we did lighting so far was called <def>forward rendering</def> or <def>forward shading</def>. A straightforward approach where we render an object and light it according to all light sources in a scene. We do this for every object individually for each object in the scene. While quite easy to understand and implement it is also quite heavy on performance as each rendered object has to iterate over each light source for every rendered fragment, which is a lot! Forward rendering also tends to waste a lot of fragment shader runs in scenes with a high depth complexity (multiple objects cover the same screen pixel) as fragment shader outputs are overwritten. +</p> + +<p> + <def>Deferred shading</def> or <def>deferred rendering</def> aims to overcome these issues by drastically changing the way we render objects. This gives us several new options to significantly optimize scenes with large numbers of lights, allowing us to render hundreds (or even thousands) of lights with an acceptable framerate. The following image is a scene with 1847 point lights rendered with deferred shading (image courtesy of Hannes Nevalainen); something that wouldn't be possible with forward rendering. +</p> + +<img src="/img/advanced-lighting/deferred_example.png" alt="Example of the power of deferred shading in OpenGL as we can easily render 1000s lights with an acceptable framerate"/> + +<p> + Deferred shading is based on the idea that we <em>defer</em> or <em>postpone</em> most of the heavy rendering (like lighting) to a later stage. Deferred shading consists of two passes: in the first pass, called the <def>geometry pass</def>, we render the scene once and retrieve all kinds of geometrical information from the objects that we store in a collection of textures called the <def>G-buffer</def>; think of position vectors, color vectors, normal vectors, and/or specular values. The geometric information of a scene stored in the <def>G-buffer</def> is then later used for (more complex) lighting calculations. Below is the content of a G-buffer of a single frame: +</p> + + <img src="/img/advanced-lighting/deferred_g_buffer.png" alt="An example of a G-Buffer filled with geometrical data of a scene in OpenGL"/> + +<p> + We use the textures from the G-buffer in a second pass called the <def>lighting pass</def> where we render a screen-filled quad and calculate the scene's lighting for each fragment using the geometrical information stored in the G-buffer; pixel by pixel we iterate over the G-buffer. Instead of taking each object all the way from the vertex shader to the fragment shader, we decouple its advanced fragment processes to a later stage. The lighting calculations are exactly the same, but this time we take all required input variables from the corresponding G-buffer textures, instead of the vertex shader (plus some uniform variables). +</p> + +<p> + The image below nicely illustrates the process of deferred shading. +</p> + + <img src="/img/advanced-lighting/deferred_overview.png" class="clean" alt="Overview of the deferred shading technique in OpenGL"/> + +<p> + A major advantage of this approach is that whatever fragment ends up in the G-buffer is the actual fragment information that ends up as a screen pixel. The depth test already concluded this fragment to be the last and top-most fragment. This ensures that for each pixel we process in the lighting pass, we only calculate lighting once. Furthermore, deferred rendering opens up the possibility for further optimizations that allow us to render a much larger amount of light sources compared to forward rendering. +</p> + +<p> + It also comes with some disadvantages though as the G-buffer requires us to store a relatively large amount of scene data in its texture color buffers. This eats memory, especially since scene data like position vectors require a high precision. Another disadvantage is that it doesn't support blending (as we only have information of the top-most fragment) and MSAA no longer works. There are several workarounds for this that we'll get to at the end of the chapter. +</p> + +<p> + Filling the G-buffer (in the geometry pass) isn't too expensive as we directly store object information like position, color, or normals into a framebuffer with a small or zero amount of processing. By using <def>multiple render targets</def> (MRT) we can even do all of this in a single render pass. +</p> + +<h2>The G-buffer</h2> +<p> + The <def>G-buffer</def> is the collective term of all textures used to store lighting-relevant data for the final lighting pass. Let's take this moment to briefly review all the data we need to light a fragment with forward rendering: +</p> + +<ul> + <li>A 3D world-space <strong>position</strong> vector to calculate the (interpolated) fragment position variable used for <var>lightDir</var> and <var>viewDir</var>. </li> + <li>An RGB diffuse <strong>color</strong> vector also known as <def>albedo</def>.</li> + <li>A 3D <strong>normal</strong> vector for determining a surface's slope.</li> + <li>A <strong>specular intensity</strong> float.</li> + <li>All light source position and color vectors.</li> + <li>The player or viewer's position vector.</li> +</ul> + +<p> + With these (per-fragment) variables at our disposal we are able to calculate the (Blinn-)Phong lighting we're accustomed to. The light source positions and colors, and the player's view position, can be configured using uniform variables, but the other variables are all fragment specific. If we can somehow pass the exact same data to the final deferred lighting pass we can calculate the same lighting effects, even though we're rendering fragments of a 2D quad. +</p> + +<p> + There is no limit in OpenGL to what we can store in a texture so it makes sense to store all per-fragment data in one or multiple screen-filled textures of the G-buffer and use these later in the lighting pass. As the G-buffer textures will have the same size as the lighting pass's 2D quad, we get the exact same fragment data we'd had in a forward rendering setting, but this time in the lighting pass; there is a one on one mapping. +</p> + +<p> + In pseudocode the entire process will look a bit like this: +</p> + +<pre><code> +while(...) // render loop +{ + // 1. geometry pass: render all geometric/color data to g-buffer + <function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, gBuffer); + <function id='13'><function id='10'>glClear</function>Color</function>(0.0, 0.0, 0.0, 1.0); // keep it black so it doesn't leak into g-buffer + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + gBufferShader.use(); + for(Object obj : Objects) + { + ConfigureShaderTransformsAndUniforms(); + obj.Draw(); + } + // 2. lighting pass: use g-buffer to calculate the scene's lighting + <function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); + lightingPassShader.use(); + BindAllGBufferTextures(); + SetLightingUniforms(); + RenderQuad(); +} +</code></pre> + +<p> + The data we'll need to store of each fragment is a <strong>position</strong> vector, a <strong>normal</strong> vector, a <strong>color</strong> vector, and a <strong>specular intensity</strong> value. In the geometry pass we need to render all objects of the scene and store these data components in the G-buffer. We can again use <def>multiple render targets</def> to render to multiple color buffers in a single render pass; this was briefly discussed in the <a href="https://learnopengl.com/Advanced-Lighting/Bloom" target="_blank">Bloom</a> chapter. +</p> + +<p> + For the geometry pass we'll need to initialize a framebuffer object that we'll call <var>gBuffer</var> that has multiple color buffers attached and a single depth renderbuffer object. For the position and normal texture we'd preferably use a high-precision texture (16 or 32-bit float per component). For the albedo and specular values we'll be fine with the default texture precision (8-bit precision per component). Note that we use <var>GL_RGBA16F</var> over <var>GL_RGB16F</var> as GPUs generally prefer 4-component formats over 3-component formats due to byte alignment; some drivers may fail to complete the framebuffer otherwise. +</p> + +<pre><code> +unsigned int gBuffer; +<function id='76'>glGenFramebuffers</function>(1, &gBuffer); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, gBuffer); +unsigned int gPosition, gNormal, gColorSpec; + +// - position color buffer +<function id='50'>glGenTextures</function>(1, &gPosition); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gPosition); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition, 0); + +// - normal color buffer +<function id='50'>glGenTextures</function>(1, &gNormal); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gNormal); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal, 0); + +// - color + specular color buffer +<function id='50'>glGenTextures</function>(1, &gAlbedoSpec); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gAlbedoSpec); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec, 0); + +// - tell OpenGL which color attachments we'll use (of this framebuffer) for rendering +unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; +glDrawBuffers(3, attachments); + +// then also add render buffer object as depth buffer and check for completeness. +[...] +</code></pre> + +<p> + Since we use multiple render targets, we have to explicitly tell OpenGL which of the color buffers associated with <var>GBuffer</var> we'd like to render to with <fun>glDrawBuffers</fun>. Also interesting to note here is we combine the color and specular intensity data in a single <code>RGBA</code> texture; this saves us from having to declare an additional color buffer texture. As your deferred shading pipeline gets more complex and needs more data you'll quickly find new ways to combine data in individual textures. +</p> + +<p> + Next we need to render into the G-buffer. Assuming each object has a diffuse, normal, and specular texture we'd use something like the following fragment shader to render into the G-buffer: +</p> + +<pre><code> +#version 330 core +layout (location = 0) out vec3 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; + +in vec2 TexCoords; +in vec3 FragPos; +in vec3 Normal; + +uniform sampler2D texture_diffuse1; +uniform sampler2D texture_specular1; + +void main() +{ + // store the fragment position vector in the first gbuffer texture + gPosition = FragPos; + // also store the per-fragment normals into the gbuffer + gNormal = normalize(Normal); + // and the diffuse per-fragment color + gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb; + // store specular intensity in gAlbedoSpec's alpha component + gAlbedoSpec.a = texture(texture_specular1, TexCoords).r; +} +</code></pre> + +<p> + As we use multiple render targets, the layout specifier tells OpenGL to which color buffer of the active framebuffer we render to. Note that we do not store the specular intensity into a single color buffer texture as we can store its single float value in the alpha component of one of the other color buffer textures. +</p> + + +<warning> + Keep in mind that with lighting calculations it is extremely important to keep all relevant variables in the same coordinate space. In this case we store (and calculate) all variables in world-space. +</warning> + +<p> + If we'd now were to render a large collection of backpack objects into the <var>gBuffer</var> framebuffer and visualize its content by projecting each color buffer one by one onto a screen-filled quad we'd see something like this: +</p> + + <img src="/img/advanced-lighting/deferred_g_buffer.png" alt="Image of a G-Buffer in OpenGL with several backpacks"/> + +<p> + Try to visualize that the world-space position and normal vectors are indeed correct. For instance, the normal vectors pointing to the right would be more aligned to a red color, similarly for position vectors that point from the scene's origin to the right. As soon as you're satisfied with the content of the G-buffer it's time to move to the next step: the lighting pass. +</p> + +<h2>The deferred lighting pass</h2> +<p> + With a large collection of fragment data in the G-Buffer at our disposal we have the option to completely calculate the scene's final lit colors. We do this by iterating over each of the G-Buffer textures pixel by pixel and use their content as input to the lighting algorithms. Because the G-buffer texture values all represent the final transformed fragment values we only have to do the expensive lighting operations once per pixel. This is especially useful in complex scenes where we'd easily invoke multiple expensive fragment shader calls per pixel in a forward rendering setting. +</p> + +<p> + For the lighting pass we're going to render a 2D screen-filled quad (a bit like a post-processing effect) and execute an expensive lighting fragment shader on each pixel: +</p> + +<pre><code> +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gPosition); +<function id='49'>glActiveTexture</function>(GL_TEXTURE1); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gNormal); +<function id='49'>glActiveTexture</function>(GL_TEXTURE2); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gAlbedoSpec); +// also send light relevant uniforms +shaderLightingPass.use(); +SendAllLightUniformsToShader(shaderLightingPass); +shaderLightingPass.setVec3("viewPos", camera.Position); +RenderQuad(); +</code></pre> + +<p> + We bind all relevant textures of the G-buffer before rendering and also send the lighting-relevant uniform variables to the shader. +</p> + +<p> + The fragment shader of the lighting pass is largely similar to the lighting chapter shaders we've used so far. What is new is the method in which we obtain the lighting's input variables, which we now directly sample from the G-buffer: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct Light { + vec3 Position; + vec3 Color; +}; +const int NR_LIGHTS = 32; +uniform Light lights[NR_LIGHTS]; +uniform vec3 viewPos; + +void main() +{ + // retrieve data from G-buffer + vec3 FragPos = texture(gPosition, TexCoords).rgb; + vec3 Normal = texture(gNormal, TexCoords).rgb; + vec3 Albedo = texture(gAlbedoSpec, TexCoords).rgb; + float Specular = texture(gAlbedoSpec, TexCoords).a; + + // then calculate lighting as usual + vec3 lighting = Albedo * 0.1; // hard-coded ambient component + vec3 viewDir = normalize(viewPos - FragPos); + for(int i = 0; i &lt; NR_LIGHTS; ++i) + { + // diffuse + vec3 lightDir = normalize(lights[i].Position - FragPos); + vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Albedo * lights[i].Color; + lighting += diffuse; + } + + FragColor = vec4(lighting, 1.0); +} +</code></pre> + +<p> + The lighting pass shader accepts 3 uniform textures that represent the G-buffer and hold all the data we've stored in the geometry pass. If we were to sample these with the current fragment's texture coordinates we'd get the exact same fragment values as if we were rendering the geometry directly. Note that we retrieve both the <var>Albedo</var> color and the <var>Specular</var> intensity from the single <var>gAlbedoSpec</var> texture. +</p> + +<p> + As we now have the per-fragment variables (and the relevant uniform variables) necessary to calculate Blinn-Phong lighting, we don't have to make any changes to the lighting code. The only thing we change in deferred shading here is the method of obtaining lighting input variables. +</p> + +<p> + Running a simple demo with a total of <code>32</code> small lights looks a bit like this: +</p> + + <img src="/img/advanced-lighting/deferred_shading.png" class="clean" alt="Example of Deferred Shading in OpenGL"/> + +<p> + One of the disadvantages of deferred shading is that it is not possible to do <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">blending</a> as all values in the G-buffer are from single fragments, and blending operates on the combination of multiple fragments. Another disadvantage is that deferred shading forces you to use the same lighting algorithm for most of your scene's lighting; you can somehow alleviate this a bit by including more material-specific data in the G-buffer. +</p> + +<p> + To overcome these disadvantages (especially blending) we often split the renderer into two parts: one deferred rendering part, and the other a forward rendering part specifically meant for blending or special shader effects not suited for a deferred rendering pipeline. To illustrate how this works, we'll render the light sources as small cubes using a forward renderer as the light cubes require a special shader (simply output a single light color). +</p> + +<h2>Combining deferred rendering with forward rendering</h2> +<p> + Say we want to render each of the light sources as a 3D cube positioned at the light source's position emitting the color of the light. A first idea that comes to mind is to simply forward render all the light sources on top of the deferred lighting quad at the end of the deferred shading pipeline. So basically render the cubes as we'd normally do, but only after we've finished the deferred rendering operations. In code this will look a bit like this: +</p> + +<pre><code> +// deferred lighting pass +[...] +RenderQuad(); + +// now render all light cubes with forward rendering as we'd normally do +shaderLightBox.use(); +shaderLightBox.setMat4("projection", projection); +shaderLightBox.setMat4("view", view); +for (unsigned int i = 0; i &lt; lightPositions.size(); i++) +{ + model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, lightPositions[i]); + model = <function id='56'>glm::scale</function>(model, glm::vec3(0.25f)); + shaderLightBox.setMat4("model", model); + shaderLightBox.setVec3("lightColor", lightColors[i]); + RenderCube(); +} +</code></pre> + +<p> + However, these rendered cubes do not take any of the stored geometry depth of the deferred renderer into account and are, as a result, always rendered on top of the previously rendered objects; this isn't the result we were looking for. +</p> + + <img src="/img/advanced-lighting/deferred_lights_no_depth.png" class="clean" alt="Image of deferred rendering with forward rendering where we didn't copy depth buffer data and lights are rendered on top of all geometry in OpenGL"/> + +<p> + What we need to do, is first copy the depth information stored in the geometry pass into the default framebuffer's depth buffer and only then render the light cubes. This way the light cubes' fragments are only rendered when on top of the previously rendered geometry. +</p> + +<p> + We can copy the content of a framebuffer to the content of another framebuffer with the help of <fun><function id='103'>glBlitFramebuffer</function></fun>, a function we also used in the <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing" target="_blank">anti-aliasing</a> chapter to resolve multisampled framebuffers. The <fun><function id='103'>glBlitFramebuffer</function></fun> function allows us to copy a user-defined region of a framebuffer to a user-defined region of another framebuffer. +</p> + +<p> + We stored the depth of all the objects rendered in the deferred geometry pass in the <var>gBuffer</var> FBO. If we were to copy the content of its depth buffer to the depth buffer of the default framebuffer, the light cubes would then render as if all of the scene's geometry was rendered with forward rendering. As briefly explained in the anti-aliasing chapter, we have to specify a framebuffer as the read framebuffer and similarly specify a framebuffer as the write framebuffer: +</p> + +<pre><code> +<function id='77'>glBindFramebuffer</function>(GL_READ_FRAMEBUFFER, gBuffer); +<function id='77'>glBindFramebuffer</function>(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer +<function id='103'>glBlitFramebuffer</function>( + 0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST +); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +// now render light cubes as before +[...] +</code></pre> + +<p> + Here we copy the entire read framebuffer's depth buffer content to the default framebuffer's depth buffer; this can similarly be done for color buffers and stencil buffers. If we then render the light cubes, the cubes indeed render correctly over the scene's geometry: +</p> + + + <img src="/img/advanced-lighting/deferred_lights_depth.png" class="clean" alt="Image of deferred rendering with forward rendering where we copied the depth buffer data and lights are rendered properly with all geometry in OpenGL"/> + +<p> + You can find the full source code of the demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/8.1.deferred_shading/deferred_shading.cpp" target="_blank">here</a>. +</p> + +<p> + With this approach we can easily combine deferred shading with forward shading. This is great as we can now still apply blending and render objects that require special shader effects, something that isn't possible in a pure deferred rendering context. +</p> + +<h2>A larger number of lights</h2> +<p> + What deferred rendering is often praised for, is its ability to render an enormous amount of light sources without a heavy cost on performance. Deferred rendering by itself doesn't allow for a very large amount of light sources as we'd still have to calculate each fragment's lighting component for each of the scene's light sources. What makes a large amount of light sources possible is a very neat optimization we can apply to the deferred rendering pipeline: that of <def>light volumes</def>. +</p> + +<p> + Normally when we render a fragment in a large lit scene we'd calculate the contribution of <strong>each</strong> light source in a scene, regardless of their distance to the fragment. A large portion of these light sources will never reach the fragment, so why waste all these lighting computations? +</p> + +<p> + The idea behind light volumes is to calculate the radius, or volume, of a light source i.e. the area where its light is able to reach fragments. As most light sources use some form of attenuation, we can use that to calculate the maximum distance or radius their light is able to reach. We then only do the expensive lighting calculations if a fragment is inside one or more of these light volumes. This can save us a considerable amount of computation as we now only calculate lighting where it's necessary. +</p> + +<p> + The trick to this approach is mostly figuring out the size or radius of the light volume of a light source. +</p> + +<h3>Calculating a light's volume or radius</h3> +<p> + To obtain a light's volume radius we have to solve the attenuation equation for when its light contribution becomes <code>0.0</code>. For the attenuation function we'll use the function introduced in the <a href="https://learnopengl.com/Lighting/Light-casters" target="_blank">light casters</a> chapter: +</p> + + \[F_{light} = \frac{I}{K_c + K_l * d + K_q * d^2}\] + +<p> + What we want to do is solve this equation for when \(F_{light}\) is <code>0.0</code>. However, this equation will never exactly reach the value <code>0.0</code>, so there won't be a solution. What we can do however, is not solve the equation for <code>0.0</code>, but solve it for a brightness value that is close to <code>0.0</code> but still perceived as dark. The brightness value of \(5/256\) would be acceptable for this chapter's demo scene; divided by 256 as the default 8-bit framebuffer can only display that many intensities per component. +</p> + +<note> + The attenuation function used is mostly dark in its visible range. If we were to limit it to an even darker brightness than \(5/256\), the light volume would become too large and thus less effective. As long as a user cannot see a sudden cut-off of a light source at its volume borders we'll be fine. Of course this always depends on the type of scene; a higher brightness threshold results in smaller light volumes and thus a better efficiency, but can produce noticeable artifacts where lighting seems to break at a volume's borders. +</note> + +<p> + The attenuation equation we have to solve becomes: +</p> + + \[\frac{5}{256} = \frac{I_{max}}{Attenuation}\] + +<p> + Here \(I_{max}\) is the light source's brightest color component. We use a light source's brightest color component as solving the equation for a light's brightest intensity value best reflects the ideal light volume radius. +</p> + +<p> + From here on we continue solving the equation: +</p> + + \[\frac{5}{256} * Attenuation = I_{max} \] + + \[5 * Attenuation = I_{max} * 256 \] + + \[Attenuation = I_{max} * \frac{256}{5} \] + + \[K_c + K_l * d + K_q * d^2 = I_{max} * \frac{256}{5} \] + + \[K_q * d^2 + K_l * d + K_c - I_{max} * \frac{256}{5} = 0 \] + +<p> + The last equation is an equation of the form \(ax^2 + bx + c = 0\), which we can solve using the quadratic equation: +</p> + + \[x = \frac{-K_l + \sqrt{K_l^2 - 4 * K_q * (K_c - I_{max} * \frac{256}{5})}}{2 * K_q} \] + +<p> + This gives us a general equation that allows us to calculate \(x\) i.e. the light volume's radius for the light source given a constant, linear, and quadratic parameter: +</p> + +<pre><code> +float constant = 1.0; +float linear = 0.7; +float quadratic = 1.8; +float lightMax = std::fmaxf(std::fmaxf(lightColor.r, lightColor.g), lightColor.b); +float radius = + (-linear + std::sqrtf(linear * linear - 4 * quadratic * (constant - (256.0 / 5.0) * lightMax))) + / (2 * quadratic); +</code></pre> + +<p> + We calculate this radius for each light source of the scene and use it to only calculate lighting for that light source if a fragment is inside the light source's volume. Below is the updated lighting pass fragment shader that takes the calculated light volumes into account. Note that this approach is merely done for teaching purposes and not viable in a practical setting as we'll soon discuss: +</p> + +<pre><code> +struct Light { + [...] + float Radius; +}; + +void main() +{ + [...] + for(int i = 0; i &lt; NR_LIGHTS; ++i) + { + // calculate distance between light source and current fragment + float distance = length(lights[i].Position - FragPos); + if(distance &lt; lights[i].Radius) + { + // do expensive lighting + [...] + } + } +} +</code></pre> + +<p> + The results are exactly the same as before, but this time each light only calculates lighting for the light sources in which volume it resides. +</p> + +<p> + You can find the final source code of the demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/8.2.deferred_shading_volumes/deferred_shading_volumes.cpp" target="_blank">here</a>. +</p> + +<h3>How we really use light volumes</h3> +<p> + The fragment shader shown above doesn't really work in practice and only illustrates how we can <em>sort of</em> use a light's volume to reduce lighting calculations. The reality is that your GPU and GLSL are pretty bad at optimizing loops and branches. The reason for this is that shader execution on the GPU is highly parallel and most architectures have a requirement that for large collection of threads they need to run the exact same shader code for it to be efficient. This often means that a shader is run that executes <strong>all</strong> branches of an <code>if</code> statement to ensure the shader runs are the same for that group of threads, making our previous <em>radius check</em> optimization completely useless; we'd still calculate lighting for all light sources! +</p> + +<p> + The appropriate approach to using light volumes is to render actual spheres, scaled by the light volume radius. The centers of these spheres are positioned at the light source's position, and as it is scaled by the light volume radius the sphere exactly encompasses the light's visible volume. This is where the trick comes in: we use the deferred lighting shader for rendering the spheres. As a rendered sphere produces fragment shader invocations that exactly match the pixels the light source affects, we only render the relevant pixels and skip all other pixels. The image below illustrates this: +</p> + + <img src="/img/advanced-lighting/deferred_light_volume_rendered.png" class="clean" alt="Image of a light volume rendered with a deferred fragment shader in OpenGL"/> + +<p> + This is done for each light source in the scene, and the resulting fragments are additively blended together. The result is then the exact same scene as before, but this time rendering only the relevant fragments per light source. This effectively reduces the computations from <code>nr_objects * nr_lights</code> to <code>nr_objects + nr_lights</code>, which makes it incredibly efficient in scenes with a large number of lights. This approach is what makes deferred rendering so suitable for rendering a large number of lights. +</p> + +<p> + There is still an issue with this approach: face culling should be enabled (otherwise we'd render a light's effect twice) and when it is enabled the user may enter a light source's volume after which the volume isn't rendered anymore (due to back-face culling), removing the light source's influence; we can solve that by only rendering the spheres' back faces. +</p> + + <p> + Rendering light volumes does take its toll on performance, and while it is generally much faster than normal deferred shading for rendering a large number of lights, there's still more we can optimize. Two other popular (and more efficient) extensions on top of deferred shading exist called <def>deferred lighting</def> and <def>tile-based deferred shading</def>. These are even more efficient at rendering large amounts of light and also allow for relatively efficient MSAA. +</p> + +<h2>Deferred rendering vs forward rendering</h2> +<p> + By itself (without light volumes), deferred shading is a nice optimization as each pixel only runs a single fragment shader, compared to forward rendering where we'd often run the fragment shader multiple times per pixel. Deferred rendering does come with a few disadvantages though: a large memory overhead, no MSAA, and blending still has to be done with forward rendering. +</p> + +<p> + When you have a small scene and not too many lights, deferred rendering is not necessarily faster and sometimes even slower as the overhead then outweighs the benefits of deferred rendering. In more complex scenes, deferred rendering quickly becomes a significant optimization; especially with the more advanced optimization extensions. In addition, some render effects (especially post-processing effects) become cheaper on a deferred render pipeline as a lot of scene inputs are already available from the g-buffer. +</p> + +<p> + As a final note I'd like to mention that basically all effects that can be accomplished with forward rendering can also be implemented in a deferred rendering context; this often only requires a small translation step. For instance, if we want to use normal mapping in a deferred renderer, we'd change the geometry pass shaders to output a world-space normal extracted from a normal map (using a TBN matrix) instead of the surface normal; the lighting calculations in the lighting pass don't need to change at all. And if you want parallax mapping to work, you'd want to first displace the texture coordinates in the geometry pass before sampling an object's diffuse, specular, and normal textures. Once you understand the idea behind deferred rendering, it's not too difficult to get creative. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html" target="_blank">Tutorial 35: Deferred Shading - Part 1</a>: a three-part deferred shading tutorial by OGLDev.</li> + <li><a href="https://software.intel.com/sites/default/files/m/d/4/1/d/8/lauritzen_deferred_shading_siggraph_2010.pdf" target="_blank">Deferred Rendering for Current and +Future Rendering Pipelines</a>: slides by Andrew Lauritzen discussing high-level tile-based deferred shading and deferred lighting.</li> + </ul> + + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Gamma-Correction.html b/pub/Advanced-Lighting/Gamma-Correction.html @@ -0,0 +1,551 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Gamma Correction</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Gamma-Correction</h1> +<p> + As soon as we compute the final pixel colors of the scene we will have to display them on a monitor. In the old days of digital imaging most monitors were cathode-ray tube (CRT) monitors. These monitors had the physical property that twice the input voltage did not result in twice the amount of brightness. Doubling the input voltage resulted in a brightness equal to an exponential relationship of roughly 2.2 known as the <def>gamma</def> of a monitor. This happens to (coincidently) also closely match how human beings measure brightness as brightness is also displayed with a similar (inverse) power relationship. To better understand what this all means take a look at the following image: +</p> + +<img src="/img/advanced-lighting/gamma_correction_brightness.png" alt="Linear encodings of display with and without gamma correction"/> + +<p> + The top line looks like the correct brightness scale to the human eye, doubling the brightness (from 0.1 to 0.2 for example) does indeed look like it's twice as bright with nice consistent differences. However, when we're talking about the physical brightness of light e.g. amount of photons leaving a light source, the bottom scale actually displays the correct brightness. At the bottom scale, doubling the brightness returns the correct physical brightness, but since our eyes perceive brightness differently (more susceptible to changes in dark colors) it looks weird. +</p> + +<p> + Because the human eyes prefer to see brightness colors according to the top scale, monitors (still today) use a power relationship for displaying output colors so that the original physical brightness colors are mapped to the non-linear brightness colors in the top scale. +</p> + +<p> + This non-linear mapping of monitors does output more pleasing brightness results for our eyes, but when it comes to rendering graphics there is one issue: all the color and brightness options we configure in our applications are based on what we perceive from the monitor and thus all the options are actually non-linear brightness/color options. Take a look at the graph below: +</p> + +<img src="/img/advanced-lighting/gamma_correction_gamma_curves.png" alt="Gamme curves"/> + +<p> + The dotted line represents color/light values in linear space and the solid line represents the color space that monitors display. If we double a color in linear space, its result is indeed double the value. For instance, take a light's color vector (0.5, 0.0, 0.0) which represents a semi-dark red light. If we would double this light in linear space it would become (1.0, 0.0, 0.0) as you can see in the graph. However, the original color gets displayed on the monitor as (0.218, 0.0, 0.0) as you can see from the graph. Here's where the issues start to rise: once we double the dark-red light in linear space, it actually becomes more than 4.5 times as bright on the monitor! +</p> + +<p> + Up until this chapter we have assumed we were working in linear space, but we've actually been working in the monitor's output space so all colors and lighting variables we configured weren't physically correct, but merely looked (sort of) right on our monitor. For this reason, we (and artists) generally set lighting values way brighter than they should be (since the monitor darkens them) which as a result makes most linear-space calculations incorrect. Note that the monitor (CRT) and linear graph both start and end at the same position; it is the intermediate values that are darkened by the display. +</p> + +<p> + Because colors are configured based on the display's output, all intermediate (lighting) calculations in linear-space are physically incorrect. This becomes more obvious as more advanced lighting algorithms are in place, as you can see in the image below: +</p> + + <img src="/img/advanced-lighting/gamma_correction_example.png" alt="Example of gamma correction w/ and without on advanced rendering"/> + +<p> + You can see that with gamma correction, the (updated) color values work more nicely together and darker areas show more details. Overall, a better image quality with a few small modifications. +</p> + +<p> + Without properly correcting this monitor gamma, the lighting looks wrong and artists will have a hard time getting realistic and good-looking results. The solution is to apply <def>gamma correction</def>. +</p> + +<h2>Gamma correction</h2> +<p> + The idea of gamma correction is to apply the inverse of the monitor's gamma to the final output color before displaying to the monitor. Looking back at the gamma curve graph earlier this chapter we see another <em>dashed</em> line that is the inverse of the monitor's gamma curve. We multiply each of the linear output colors by this inverse gamma curve (making them brighter) and as soon as the colors are displayed on the monitor, the monitor's gamma curve is applied and the resulting colors become linear. We effectively brighten the intermediate colors so that as soon as the monitor darkens them, it balances all out. +</p> + + +<p> + Let's give another example. Say we again have the dark-red color \((0.5, 0.0, 0.0)\). Before displaying this color to the monitor we first apply the gamma correction curve to the color value. Linear colors displayed by a monitor are roughly scaled to a power of \(2.2\) so the inverse requires scaling the colors by a power of \(1/2.2\). The gamma-corrected dark-red color thus becomes \((0.5, 0.0, 0.0)^{1/2.2} = (0.5, 0.0, 0.0)^{0.45} = (0.73, 0.0, 0.0)\). The corrected colors are then fed to the monitor and as a result the color is displayed as \((0.73, 0.0, 0.0)^{2.2} = (0.5, 0.0, 0.0)\). You can see that by using gamma-correction, the monitor now finally displays the colors as we linearly set them in the application. +</p> + +<note> +A gamma value of 2.2 is a default gamma value that roughly estimates the average gamma of most displays. The color space as a result of this gamma of 2.2 is called the <def>sRGB</def> color space (not 100% exact, but close). Each monitor has their own gamma curves, but a gamma value of 2.2 gives good results on most monitors. For this reason, games often allow players to change the game's gamma setting as it varies slightly per monitor. +</note> + +<p> + There are two ways to apply gamma correction to your scene: +</p> + +<ul> + <li>By using OpenGL's built-in sRGB framebuffer support.</li> + <li>By doing the gamma correction ourselves in the fragment shader(s).</li> +</ul> + +<p> + The first option is probably the easiest, but also gives you less control. By enabling <var>GL_FRAMEBUFFER_SRGB</var> you tell OpenGL that each subsequent drawing command should first gamma correct colors (from the sRGB color space) before storing them in color buffer(s). The sRGB is a color space that roughly corresponds to a gamma of 2.2 and a standard for most devices. After enabling <var>GL_FRAMEBUFFER_SRGB</var>, OpenGL automatically performs gamma correction after each fragment shader run to all subsequent framebuffers, including the default framebuffer. +</p> + +<p> + Enabling <var>GL_FRAMEBUFFER_SRGB</var> is as simple as calling <fun><function id='60'>glEnable</function></fun>: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_FRAMEBUFFER_SRGB); +</code></pre> + +<p> + From now on your rendered images will be gamma corrected and as this is done by the hardware it is completely free. Something you should keep in mind with this approach (and the other approach) is that gamma correction (also) transforms the colors from linear space to non-linear space so it is very important you only do gamma correction at the last and final step. If you gamma-correct your colors before the final output, all subsequent operations on those colors will operate on incorrect values. For instance, if you use multiple framebuffers you probably want intermediate results passed in between framebuffers to remain in linear-space and only have the last framebuffer apply gamma correction before being sent to the monitor. +</p> + +<p> + The second approach requires a bit more work, but also gives us complete control over the gamma operations. We apply gamma correction at the end of each relevant fragment shader run so the final colors end up gamma corrected before being sent out to the monitor: +</p> + +<pre><code> +void main() +{ + // do super fancy lighting in linear space + [...] + // apply gamma correction + float gamma = 2.2; + FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma)); +} +</code></pre> + +<p> + The last line of code effectively raises each individual color component of <var>fragColor</var> to <code>1.0/gamma</code>, correcting the output color of this fragment shader run. +</p> + +<p> + An issue with this approach is that in order to be consistent you have to apply gamma correction to each fragment shader that contributes to the final output. If you have a dozen fragment shaders for multiple objects, you have to add the gamma correction code to each of these shaders. An easier solution would be to introduce a post-processing stage in your render loop and apply gamma correction on the post-processed quad as a final step which you'd only have to do once. +</p> + +<p> + That one line represents the technical implementation of gamma correction. Not all too impressive, but there are a few extra things you have to consider when doing gamma correction. +</p> + + +<h2>sRGB textures</h2> +<p> + Because monitors display colors with gamma applied, whenever you draw, edit, or paint a picture on your computer you are picking colors based on what you see on the monitor. This effectively means all the pictures you create or edit are not in linear space, but in sRGB space e.g. doubling a dark-red color on your screen based on perceived brightness, does not equal double the red component. +</p> + +<p> + As a result, when texture artists create art by eye, all the textures' values are in sRGB space so if we use those textures as they are in our rendering application we have to take this into account. Before we knew about gamma correction this wasn't really an issue, because the textures looked good in sRGB space which is the same space we worked in; the textures were displayed exactly as they are which was fine. However, now that we're displaying everything in linear space, the texture colors will be off as the following image shows: +</p> + +<img src="/img/advanced-lighting/gamma_correction_srgbtextures.png" alt="Comparrison between working in linear space with sRGB textures and linear-space textures"/> + +<p> + The texture image is way too bright and this happens because it is actually gamma corrected twice! Think about it, when we create an image based on what we see on the monitor, we effectively gamma correct the color values of an image so that it looks right on the monitor. Because we then again gamma correct in the renderer, the image ends up way too bright. +</p> + +<p> + To fix this issue we have to make sure texture artists work in linear space. However, since it's easier to work in sRGB space and most tools don't even properly support linear texturing, this is probably not the preferred solution. +</p> + +<p> + The other solution is to re-correct or transform these sRGB textures to linear space before doing any calculations on their color values. We can do this as follows: +</p> + +<pre><code> +float gamma = 2.2; +vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma)); +</code></pre> + +<p> + To do this for each texture in sRGB space is quite troublesome though. Luckily OpenGL gives us yet another solution to our problems by giving us the <var>GL_SRGB</var> and <var>GL_SRGB_ALPHA</var> internal texture formats. +</p> + +<p> + If we create a texture in OpenGL with any of these two sRGB texture formats, OpenGL will automatically correct the colors to linear-space as soon as we use them, allowing us to properly work in linear space. We can specify a texture as an sRGB texture as follows: +</p> + +<pre><code> +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); +</code></pre> + +<p> + If you also want to include alpha components in your texture you'll have to specify the texture's internal format as <var>GL_SRGB_ALPHA</var>. +</p> + +<p> + You should be careful when specifying your textures in sRGB space as not all textures will actually be in sRGB space. Textures used for coloring objects (like diffuse textures) are almost always in sRGB space. Textures used for retrieving lighting parameters (like <a href="https://learnopengl.com/Lighting/Lighting-maps" target="_blank">specular maps</a> and <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" target="_blank">normal maps</a>) are almost always in linear space, so if you were to configure these as sRGB textures the lighting will look odd. Be careful in which textures you specify as sRGB. +</p> + +<p> + With our diffuse textures specified as sRGB textures you get the visual output you'd expect again, but this time everything is gamma corrected only once. +</p> + +<h2>Attenuation</h2> +<p> + Something else that's different with gamma correction is lighting attenuation. In the real physical world, lighting attenuates closely inversely proportional to the squared distance from a light source. In normal English it simply means that the light strength is reduced over the distance to the light source squared, like below: +</p> + +<pre><code> +float attenuation = 1.0 / (distance * distance); +</code></pre> + +<p> + However, when using this equation the attenuation effect is usually way too strong, giving lights a small radius that doesn't look physically right. For that reason other attenuation functions were used (like we discussed in the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">basic lighting</a> chapter) that give much more control, or the linear equivalent is used: +</p> + +<pre><code> +float attenuation = 1.0 / distance; +</code></pre> + +<p> + The linear equivalent gives more plausible results compared to its quadratic variant without gamma correction, but when we enable gamma correction the linear attenuation looks too weak and the physically correct quadratic attenuation suddenly gives the better results. The image below shows the differences: +</p> + + <img src="/img/advanced-lighting/gamma_correction_attenuation.png" alt="Attenuation differences between gamma corrected and uncorrected scene."/> + +<p> + The cause of this difference is that light attenuation functions change brightness, and as we weren't visualizing our scene in linear space we chose the attenuation functions that looked best on our monitor, but weren't physically correct. Think of the squared attenuation function: if we were to use this function without gamma correction, the attenuation function effectively becomes: \((1.0 / distance^2)^{2.2}\) when displayed on a monitor. This creates a much larger attenuation from what we originally anticipated. This also explains why the linear equivalent makes much more sense without gamma correction as this effectively becomes \((1.0 / distance)^{2.2} = 1.0 / distance^{2.2}\) which resembles its physical equivalent a lot more. +</p> + +<note> + The more advanced attenuation function we discussed in the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">basic lighting</a> chapter still has its place in gamma corrected scenes as it gives more control over the exact attenuation (but of course requires different parameters in a gamma corrected scene). +</note> + +<p> + You can find the source code of this simple demo scene <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/2.gamma_correction/gamma_correction.cpp" target="_blank">here</a>. By pressing the spacebar we switch between a gamma corrected and un-corrected scene with both scenes using their texture and attenuation equivalents. It's not the most impressive demo, but it does show how to actually apply all techniques. +</p> + +<p> + To summarize, gamma correction allows us to do all our shader/lighting calculations in linear space. Because linear space makes sense in the physical world, most physical equations now actually give good results (like real light attenuation). The more advanced your lighting becomes, the easier it is to get good looking (and realistic) results with gamma correction. That is also why it's advised to only really tweak your lighting parameters as soon as you have gamma correction in place. +</p> + + +<h2>Additional resources</h2> +<ul> + <li><a href="http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/" target="_blank">What every coder should know about gamma</a>: a well written in-depth article by John Novak about gamma correction.</li> + <li><a href="http://www.cambridgeincolour.com/tutorials/gamma-correction.htm" target="_blank">www.cambridgeincolour.com</a>: more about gamma and gamma correction. </li> + <li><a href="http://blog.wolfire.com/2010/02/Gamma-correct-lighting" target="_blank">blog.wolfire.com</a>: blog post by David Rosen about the benefit of gamma correction in graphics rendering. </li> + <li><a href="http://renderwonk.com/blog/index.php/archive/adventures-with-gamma-correct-rendering/" target="_blank">renderwonk.com</a>: some extra practical considerations. </li> + +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/HDR.html b/pub/Advanced-Lighting/HDR.html @@ -0,0 +1,553 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">HDR</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/HDR</h1> +<p> + Brightness and color values, by default, are clamped between <code>0.0</code> and <code>1.0</code> when stored into a framebuffer. This, at first seemingly innocent, statement caused us to always specify light and color values somewhere in this range, trying to make them fit into the scene. This works oké and gives decent results, but what happens if we walk in a really bright area with multiple bright light sources that as a total sum exceed <code>1.0</code>? The answer is that all fragments that have a brightness or color sum over <code>1.0</code> get clamped to <code>1.0</code>, which isn't pretty to look at: +</p> + +<img src="/img/advanced-lighting/hdr_clamped.png" class="clean" alt="Color values clamped in bright areas"/> + +<p> + Due to a large number of fragments' color values getting clamped to <code>1.0</code>, each of the bright fragments have the exact same white color value in large regions, losing a significant amount of detail and giving it a fake look. +</p> + +<p> + A solution to this problem would be to reduce the strength of the light sources and ensure no area of fragments in your scene ends up brighter than <code>1.0</code>; this is not a good solution as this forces you to use unrealistic lighting parameters. A better approach is to allow color values to temporarily exceed <code>1.0</code> and transform them back to the original range of <code>0.0</code> and <code>1.0</code> as a final step, but without losing detail. +</p> + +<p> + Monitors (non-HDR) are limited to display colors in the range of <code>0.0</code> and <code>1.0</code>, but there is no such limitation in lighting equations. By allowing fragment colors to exceed <code>1.0</code> we have a much higher range of color values available to work in known as <def>high dynamic range</def> (HDR). With high dynamic range, bright things can be really bright, dark things can be really dark, and details can be seen in both. +</p> + +<p> + High dynamic range was originally only used for photography where a photographer takes multiple pictures of the same scene with varying exposure levels, capturing a large range of color values. Combining these forms a HDR image where a large range of details are visible based on the combined exposure levels, or a specific exposure it is viewed with. For instance, the following image (credits to Colin Smith) shows a lot of detail at brightly lit regions with a low exposure (look at the window), but these details are gone with a high exposure. However, a high exposure now reveals a great amount of detail at darker regions that weren't previously visible. +</p> + + <img src="/img/advanced-lighting/hdr_image.png" alt="HDR image showing multiple exposure levels and their respective details"/> + +<p> + This is also very similar to how the human eye works and the basis of high dynamic range rendering. When there is little light, the human eye adapts itself so the darker parts become more visible and similarly for bright areas. It's like the human eye has an automatic exposure slider based on the scene's brightness. +</p> + +<p> + High dynamic range rendering works a bit like that. We allow for a much larger range of color values to render to, collecting a large range of dark and bright details of a scene, and at the end we transform all the HDR values back to the <def>low dynamic range</def> (LDR) of [<code>0.0</code>, <code>1.0</code>]. This process of converting HDR values to LDR values is called <def>tone mapping</def> and a large collection of tone mapping algorithms exist that aim to preserve most HDR details during the conversion process. These tone mapping algorithms often involve an exposure parameter that selectively favors dark or bright regions. +</p> + +<p> + When it comes to real-time rendering, high dynamic range allows us to not only exceed the LDR range of [<code>0.0</code>, <code>1.0</code>] and preserve more detail, but also gives us the ability to specify a light source's intensity by their <em>real</em> intensities. For instance, the sun has a much higher intensity than something like a flashlight so why not configure the sun as such (e.g. a diffuse brightness of <code>100.0</code>). This allows us to more properly configure a scene's lighting with more realistic lighting parameters, something that wouldn't be possible with LDR rendering as they'd then directly get clamped to <code>1.0</code>. +</p> + +<p> + As (non-HDR) monitors only display colors in the range between <code>0.0</code> and <code>1.0</code> we do need to transform the currently high dynamic range of color values back to the monitor's range. Simply re-transforming the colors back with a simple average wouldn't do us much good as brighter areas then become a lot more dominant. What we can do, is use different equations and/or curves to transform the HDR values back to LDR that give us complete control over the scene's brightness. This is the process earlier denoted as tone mapping and the final step of HDR rendering. +</p> + +<h2>Floating point framebuffers</h2> +<p> + To implement high dynamic range rendering we need some way to prevent color values getting clamped after each fragment shader run. When framebuffers use a normalized fixed-point color format (like <var>GL_RGB</var>) as their color buffer's internal format, OpenGL automatically clamps the values between <code>0.0</code> and <code>1.0</code> before storing them in the framebuffer. This operation holds for most types of framebuffer formats, except for floating point formats. +</p> + +<p> + When the internal format of a framebuffer's color buffer is specified as <var>GL_RGB16F</var>, <var>GL_RGBA16F</var>, <var>GL_RGB32F</var>, or <var>GL_RGBA32F</var> the framebuffer is known as a <def>floating point framebuffer</def> that can store floating point values outside the default range of <code>0.0</code> and <code>1.0</code>. This is perfect for rendering in high dynamic range! +</p> + +<p> + To create a floating point framebuffer the only thing we need to change is its color buffer's internal format parameter: +</p> + +<pre><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, colorBuffer); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); +</code></pre> + +<p> + The default framebuffer of OpenGL (by default) only takes up 8 bits per color component. With a floating point framebuffer with 32 bits per color component (when using <var>GL_RGB32F</var> or <var>GL_RGBA32F</var>) we're using 4 times more memory for storing color values. As 32 bits isn't really necessary (unless you need a high level of precision) using <var>GL_RGBA16F</var> will suffice. +</p> + +<p> + With a floating point color buffer attached to a framebuffer we can now render the scene into this framebuffer knowing color values won't get clamped between <code>0.0</code> and <code>1.0</code>. In this chapter's example demo we first render a lit scene into the floating point framebuffer and then display the framebuffer's color buffer on a screen-filled quad; it'll look a bit like this: +</p> + +<pre><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, hdrFBO); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // [...] render (lit) scene +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); + +// now render hdr color buffer to 2D screen-filling quad with tone mapping shader +hdrShader.use(); +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, hdrColorBufferTexture); +RenderQuad(); +</code></pre> + +<p> + Here a scene's color values are filled into a floating point color buffer which can contain any arbitrary color value, possibly exceeding <code>1.0</code>. For this chapter, a simple demo scene was created with a large stretched cube acting as a tunnel with four point lights, one being extremely bright positioned at the tunnel's end: +</p> + +<pre><code> +std::vector&lt;glm::vec3&gt; lightColors; +lightColors.push_back(glm::vec3(200.0f, 200.0f, 200.0f)); +lightColors.push_back(glm::vec3(0.1f, 0.0f, 0.0f)); +lightColors.push_back(glm::vec3(0.0f, 0.0f, 0.2f)); +lightColors.push_back(glm::vec3(0.0f, 0.1f, 0.0f)); +</code></pre> + +<p> + Rendering to a floating point framebuffer is exactly the same as we would normally render into a framebuffer. What is new is <var>hdrShader</var>'s fragment shader that renders the final 2D quad with the floating point color buffer texture attached. Let's first define a simple pass-through fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D hdrBuffer; + +void main() +{ + vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; + FragColor = vec4(hdrColor, 1.0); +} +</code></pre> + +<p> + Here we directly sample the floating point color buffer and use its color value as the fragment shader's output. However, as the 2D quad's output is directly rendered into the default framebuffer, all the fragment shader's output values will still end up clamped between <code>0.0</code> and <code>1.0</code> even though we have several values in the floating point color texture exceeding <code>1.0</code>. +</p> + + <img src="/img/advanced-lighting/hdr_direct.png" alt="Direct rendering of floating point color values to the default framebuffer without tone mapping."/> + +<p> + It becomes clear the intense light values at the end of the tunnel are clamped to <code>1.0</code> as a large portion of it is completely white, effectively losing all lighting details in the process. As we directly write HDR values to an LDR output buffer it is as if we have no HDR enabled in the first place. What we need to do is transform all the floating point color values into the <code>0.0</code> - <code>1.0</code> range without losing any of its details. We need to apply a process called <def>tone mapping</def>. +</p> + +<h2>Tone mapping</h2> +<p> + Tone mapping is the process of transforming floating point color values to the expected [<code>0.0</code>, <code>1.0</code>] range known as low dynamic range without losing too much detail, often accompanied with a specific stylistic color balance. +</p> + +<p> + One of the more simple tone mapping algorithms is <def>Reinhard tone mapping</def> that involves dividing the entire HDR color values to LDR color values. The Reinhard tone mapping algorithm evenly balances out all brightness values onto LDR. We include Reinhard tone mapping into the previous fragment shader and also add a <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction" target="_blank">gamma correction</a> filter for good measure (including the use of sRGB textures): +</p> + +<pre><code> +void main() +{ + const float gamma = 2.2; + vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; + + // reinhard tone mapping + vec3 mapped = hdrColor / (hdrColor + vec3(1.0)); + // gamma correction + mapped = pow(mapped, vec3(1.0 / gamma)); + + FragColor = vec4(mapped, 1.0); +} +</code></pre> + +<p> + With Reinhard tone mapping applied we no longer lose any detail at the bright areas of our scene. It does tend to slightly favor brighter areas, making darker regions seem less detailed and distinct: +</p> + + <img src="/img/advanced-lighting/hdr_reinhard.png" class="clean" alt="Reinhard tone mapping algorithm applied with HDR rendering in OpenGL"/> + +<p> + Here you can again see details at the end of the tunnel as the wood texture pattern becomes visible again. With this relatively simple tone mapping algorithm we can properly see the entire range of HDR values stored in the floating point framebuffer, giving us precise control over the scene's lighting without losing details. +</p> + +<note> + Note that we could also directly tone map at the end of our lighting shader, not needing any floating point framebuffer at all! However, as scenes get more complex you'll frequently find the need to store intermediate HDR results as floating point buffers so this is a good exercise. +</note> + +<p> + Another interesting use of tone mapping is to allow the use of an exposure parameter. You probably remember from the introduction that HDR images contain a lot of details visible at different exposure levels. If we have a scene that features a day and night cycle it makes sense to use a lower exposure at daylight and a higher exposure at night time, similar to how the human eye adapts. With such an exposure parameter it allows us to configure lighting parameters that work both at day and night under different lighting conditions as we only have to change the exposure parameter. +</p> + +<p> + A relatively simple exposure tone mapping algorithm looks as follows: +</p> + +<pre><code> +uniform float exposure; + +void main() +{ + const float gamma = 2.2; + vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb; + + // exposure tone mapping + vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure); + // gamma correction + mapped = pow(mapped, vec3(1.0 / gamma)); + + FragColor = vec4(mapped, 1.0); +} +</code></pre> + +<p> + Here we defined an <var>exposure</var> uniform that defaults at <code>1.0</code> and allows us to more precisely specify whether we'd like to focus more on dark or bright regions of the HDR color values. For instance, with high exposure values the darker areas of the tunnel show significantly more detail. In contrast, a low exposure largely removes the dark region details, but allows us to see more detail in the bright areas of a scene. Take a look at the image below to see the tunnel at multiple exposure levels: +</p> + + <img src="/img/advanced-lighting/hdr_exposure.png" alt="Multiple exposure levels of HDR tone mapping in OpenGL"/> + +<p> + This image clearly shows the benefit of high dynamic range rendering. By changing the exposure level we get to see a lot of details of our scene, that would've been otherwise lost with low dynamic range rendering. Take the end of the tunnel for example. With a normal exposure the wood structure is barely visible, but with a low exposure the detailed wooden patterns are clearly visible. The same holds for the wooden patterns close by that are more visible with a high exposure. +</p> + +<p> + You can find the source code of the demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/6.hdr/hdr.cpp" target="_blank">here</a>. +</p> + +<h3>More HDR</h3> +<p> + The two tone mapping algorithms shown are only a few of a large collection of (more advanced) tone mapping algorithms of which each has their own strengths and weaknesses. Some tone mapping algorithms favor certain colors/intensities above others and some algorithms display both the low and high exposure colors at the same time to create more colorful and detailed images. There is also a collection of techniques known as <def>automatic exposure adjustment</def> or <def>eye adaptation</def> techniques that determine the brightness of the scene in the previous frame and (slowly) adapt the exposure parameter such that the scene gets brighter in dark areas or darker in bright areas mimicking the human eye. +</p> + +<p> + The real benefit of HDR rendering really shows itself in large and complex scenes with heavy lighting algorithms. As it is difficult to create such a complex demo scene for teaching purposes while keeping it accessible, the chapter's demo scene is small and lacks detail. While relatively simple it does show some of the benefits of HDR rendering: no details are lost in high and dark regions as they can be restored with tone mapping, the addition of multiple lights doesn't cause clamped regions, and light values can be specified by real brightness values not being limited by LDR values. Furthermore, HDR rendering also makes several other interesting effects more feasible and realistic; one of these effects is <def>bloom</def> that we'll discuss in the next <a href="https://learnopengl.com/Advanced-Lighting/Bloom" target="_blank">next</a> chapter. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://gamedev.stackexchange.com/questions/62836/does-hdr-rendering-have-any-benefits-if-bloom-wont-be-applied" target="_blank">Does HDR rendering have any benefits if bloom won't be applied?</a>: a stackexchange question that features a great lengthy answer describing some of the benefits of HDR rendering.</li> + <li><a href="http://photo.stackexchange.com/questions/7630/what-is-tone-mapping-how-does-it-relate-to-hdr" target="_blank">What is tone mapping? How does it relate to HDR?</a>: another interesting answer with great reference images to explain tone mapping.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Normal-Mapping.html b/pub/Advanced-Lighting/Normal-Mapping.html @@ -0,0 +1,858 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Normal Mapping</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Normal-Mapping</h1> +<p> + All of our scenes are filled with meshes, each consisting of hundreds or maybe thousands of triangles. We boosted the realism by wrapping 2D textures on these flat triangles, hiding the fact that the polygons are just tiny flat triangles. Textures help, but when you take a good close look at the meshes it is still quite easy to see the underlying flat surfaces. Most real-life surface aren't flat however and exhibit a lot of (bumpy) details. +</p> + +<p> + For instance, take a brick surface. A brick surface is quite a rough surface and obviously not completely flat: it contains sunken cement stripes and a lot of detailed little holes and cracks. If we were to view such a brick surface in a lit scene the immersion gets easily broken. Below we can see a brick texture applied to a flat surface lit by a point light. +</p> + +<img src="/img/advanced-lighting/normal_mapping_flat.png" class="clean" alt="Brick surface lighted by point light in OpenGL. It's not too realistic; its flat structures is now quite obvious"/> + +<p> + The lighting doesn't take any of the small cracks and holes into account and completely ignores the deep stripes between the bricks; the surface looks perfectly flat. We can partly fix the flat look by using a specular map to pretend some surfaces are less lit due to depth or other details, but that's more of a hack than a real solution. What we need is some way to inform the lighting system about all the little depth-like details of the surface. +</p> + +<p> + If we think about this from a light's perspective: how comes the surface is lit as a completely flat surface? The answer is the surface's normal vector. From the lighting technique's point of view, the only way it determines the shape of an object is by its perpendicular normal vector. The brick surface only has a single normal vector, and as a result the surface is uniformly lit based on this normal vector's direction. What if we, instead of a per-surface normal that is the same for each fragment, use a per-fragment normal that is different for each fragment? This way we can slightly deviate the normal vector based on a surface's little details; this gives the illusion the surface is a lot more complex: +</p> + + <img src="/img/advanced-lighting/normal_mapping_surfaces.png" class="clean" alt="Surfaces displaying per-surface normal and per-fragment normals for normal mapping in OpenGL"/> + +<p> + By using per-fragment normals we can trick the lighting into believing a surface consists of tiny little planes (perpendicular to the normal vectors) giving the surface an enormous boost in detail. This technique to use per-fragment normals compared to per-surface normals is called <def>normal mapping</def> or <def>bump mapping</def>. Applied to the brick plane it looks a bit like this: +</p> + +<img src="/img/advanced-lighting/normal_mapping_compare.png" alt="Surface without and with normal mapping in OpenGL"/> + +<p> + As you can see, it gives an enormous boost in detail and for a relatively low cost. Since we only change the normal vectors per fragment there is no need to change the lighting equation. We now pass a per-fragment normal, instead of an interpolated surface normal, to the lighting algorithm. The lighting then does the rest. +</p> + +<h2>Normal mapping</h2> +<p> + To get normal mapping to work we're going to need a per-fragment normal. Similar to what we did with diffuse and specular maps we can use a 2D texture to store per-fragment normal data. This way we can sample a 2D texture to get a normal vector for that specific fragment. + </p> + + <p> + While normal vectors are geometric entities and textures are generally only used for color information, storing normal vectors in a texture may not be immediately obvious. If you think about color vectors in a texture they are represented as a 3D vector with an <code>r</code>, <code>g</code>, and <code>b</code> component. We can similarly store a normal vector's <code>x</code>, <code>y</code> and <code>z</code> component in the respective color components. Normal vectors range between <code>-1</code> and <code>1</code> so they're first mapped to [<code>0</code>,<code>1</code>]: +</p> + +<pre><code> +vec3 rgb_normal = normal * 0.5 + 0.5; // transforms from [-1,1] to [0,1] +</code></pre> + +<p> + With normal vectors transformed to an RGB color component like this, we can store a per-fragment normal derived from the shape of a surface onto a 2D texture. An example <def>normal map</def> of the brick surface at the start of this chapter is shown below: +</p> + + <img src="/img/advanced-lighting/normal_mapping_normal_map.png" alt="Image of a normal map in OpenGL normal mapping"/> + +<p> + This (and almost all normal maps you find online) will have a blue-ish tint. This is because the normals are all closely pointing outwards towards the positive z-axis \((0, 0, 1)\): a blue-ish color. The deviations in color represent normal vectors that are slightly offset from the general positive z direction, giving a sense of depth to the texture. For example, you can see that at the top of each brick the color tends to be more greenish, which makes sense as the top side of a brick would have normals pointing more in the positive y direction \((0, 1, 0)\) which happens to be the color green! +</p> + +<p> + With a simple plane, looking at the positive z-axis, we can take <a href="/img/textures/brickwall.jpg" target="_blank">this</a> diffuse texture and <a href="/img/textures/brickwall_normal.jpg" target="_blank">this</a> normal map to render the image from the previous section. Note that the linked normal map is different from the one shown above. The reason for this is that OpenGL reads texture coordinates with the y (or v) coordinate reversed from how textures are generally created. The linked normal map thus has its y (or green) component inversed (you can see the green colors are now pointing downwards); if you fail to take this into account, the lighting will be incorrect. Load both textures, bind them to the proper texture units, and render a plane with the following changes in the lighting fragment shader: +</p> + +<pre><code> +uniform sampler2D normalMap; + +void main() +{ + // obtain normal from normal map in range [0,1] + normal = texture(normalMap, fs_in.TexCoords).rgb; + // transform normal vector to range [-1,1] + normal = normalize(normal * 2.0 - 1.0); + + [...] + // proceed with lighting as normal +} +</code></pre> + +<p> + Here we reverse the process of mapping normals to RGB colors by remapping the sampled normal color from [<code>0</code>,<code>1</code>] back to [<code>-1</code>,<code>1</code>] and then use the sampled normal vectors for the upcoming lighting calculations. In this case we used a Blinn-Phong shader. +</p> + +<p> + By slowly moving the light source over time you really get a sense of depth using the normal map. Running this normal mapping example gives the exact results as shown at the start of this chapter: +</p> + +<img src="/img/advanced-lighting/normal_mapping_correct.png" class="clean" alt="Surface without and with normal mapping in OpenGL"/> + +<p> + There is one issue however that greatly limits this use of normal maps. The normal map we used had normal vectors that all pointed somewhat in the positive z direction. This worked because the plane's surface normal was also pointing in the positive z direction. However, what would happen if we used the same normal map on a plane laying on the ground with a surface normal vector pointing in the positive y direction? +</p> + + <img src="/img/advanced-lighting/normal_mapping_ground.png" class="clean" alt="Image of plane with normal mapping without tangent space transformation, looks off in OpenGL"/> + +<p> + The lighting doesn't look right! This happens because the sampled normals of this plane still roughly point in the positive z direction even though they should mostly point in the positive y direction. As a result, the lighting thinks the surface's normals are the same as before when the plane was pointing towards the positive z direction; the lighting is incorrect. The image below shows what the sampled normals approximately look like on this surface: +</p> + + <img src="/img/advanced-lighting/normal_mapping_ground_normals.png" class="clean" alt="Image of plane with normal mapping without tangent space transformation with displayed normals, looks off in OpenGL"/> + +<p> + You can see that all the normals point somewhat in the positive z direction even though they should be pointing towards the positive y direction. One solution to this problem is to define a normal map for each possible direction of the surface; in the case of a cube we would need 6 normal maps. However, with advanced meshes that can have more than hundreds of possible surface directions this becomes an infeasible approach. +</p> + +<p> + A different solution exists that does all the lighting in a different coordinate space: a coordinate space where the normal map vectors always point towards the positive z direction; all other lighting vectors are then transformed relative to this positive z direction. This way we can always use the same normal map, regardless of orientation. This coordinate space is called <def>tangent space</def>. +</p> + +<h2>Tangent space</h2> +<p> + Normal vectors in a normal map are expressed in tangent space where normals always point roughly in the positive z direction. Tangent space is a space that's local to the surface of a triangle: the normals are relative to the local reference frame of the individual triangles. Think of it as the local space of the normal map's vectors; they're all defined pointing in the positive z direction regardless of the final transformed direction. Using a specific matrix we can then transform normal vectors from this <em>local</em> tangent space to world or view coordinates, orienting them along the final mapped surface's direction. +</p> + +<p> + Let's say we have the incorrect normal mapped surface from the previous section looking in the positive y direction. The normal map is defined in tangent space, so one way to solve the problem is to calculate a matrix to transform normals from tangent space to a different space such that they're aligned with the surface's normal direction: the normal vectors are then all pointing roughly in the positive y direction. The great thing about tangent space is that we can calculate this matrix for any type of surface so that we can properly align the tangent space's z direction to the surface's normal direction. +</p> + +<p> + Such a matrix is called a <def>TBN</def> matrix where the letters depict a <def>Tangent</def>, <def>Bitangent</def> and <def>Normal</def> vector. These are the vectors we need to construct this matrix. To construct such a <em>change-of-basis</em> matrix, that transforms a tangent-space vector to a different coordinate space, we need three perpendicular vectors that are aligned along the surface of a normal map: an up, right, and forward vector; similar to what we did in the <a href="https://learnopengl.com/Getting-Started/Camera" target="_blank">camera</a> chapter. +</p> + +<p> + We already know the up vector, which is the surface's normal vector. The right and forward vector are the tangent and bitangent vector respectively. The following image of a surface shows all three vectors on a surface: +</p> + + <img src="/img/advanced-lighting/normal_mapping_tbn_vectors.png" class="clean" alt="Normal mapping tangent, bitangent and normal vectors on a surface in OpenGL"/> + +<p> + Calculating the tangent and bitangent vectors is not as straightforward as the normal vector. We can see from the image that the direction of the normal map's tangent and bitangent vector align with the direction in which we define a surface's texture coordinates. We'll use this fact to calculate tangent and bitangent vectors for each surface. Retrieving them does require a bit of math; take a look at the following image: +</p> + + <img src="/img/advanced-lighting/normal_mapping_surface_edges.png" class="clean" alt="Edges of a surface in OpenGL required for calculating TBN matrix"/> + +<p> + From the image we can see that the texture coordinate differences of an edge \(E_2\) of a triangle (denoted as \(\Delta U_2\) and \(\Delta V_2\)) are expressed in the same direction as the tangent vector \(T\) and bitangent vector \(B\). Because of this we can write both displayed edges \(E_1\) and \(E_2\) of the triangle as a linear combination of the tangent vector \(T\) and the bitangent vector \(B\): +</p> + + \[E_1 = \Delta U_1T + \Delta V_1B\] + \[E_2 = \Delta U_2T + \Delta V_2B\] + +<p> + Which we can also write as: +</p> + + \[(E_{1x}, E_{1y}, E_{1z}) = \Delta U_1(T_x, T_y, T_z) + \Delta V_1(B_x, B_y, B_z)\] + \[(E_{2x}, E_{2y}, E_{2z}) = \Delta U_2(T_x, T_y, T_z) + \Delta V_2(B_x, B_y, B_z)\] + +<p> + We can calculate \(E\) as the difference vector between two triangle positions, and \(\Delta U\) and \(\Delta V\) as their texture coordinate differences. We're then left with two unknowns (tangent \(T\) and bitangent \(B\)) and two equations. You may remember from your algebra classes that this allows us to solve for \(T\) and \(B\). +</p> + +<p> + The last equation allows us to write it in a different form: that of matrix multiplication: +</p> + + \[\begin{bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end{bmatrix} = \begin{bmatrix} \Delta U_1 & \Delta V_1 \\ \Delta U_2 & \Delta V_2 \end{bmatrix} \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} \] + +<p> + Try to visualize the matrix multiplications in your head and confirm that this is indeed the same equation. An advantage of rewriting the equations in matrix form is that solving for \(T\) and \(B\) is easier to understand. If we multiply both sides of the equations by the inverse of the \(\Delta U \Delta V\) matrix we get: +</p> + + \[ \begin{bmatrix} \Delta U_1 & \Delta V_1 \\ \Delta U_2 & \Delta V_2 \end{bmatrix}^{-1} \begin{bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end{bmatrix} = \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} \] + +<p> + This allows us to solve for \(T\) and \(B\). This does require us to calculate the inverse of the delta texture coordinate matrix. I won't go into the mathematical details of calculating a matrix' inverse, but it roughly translates to 1 over the determinant of the matrix, multiplied by its adjugate matrix: +</p> + \[ \begin{bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end{bmatrix} = \frac{1}{\Delta U_1 \Delta V_2 - \Delta U_2 \Delta V_1} \begin{bmatrix} \Delta V_2 & -\Delta V_1 \\ -\Delta U_2 & \Delta U_1 \end{bmatrix} \begin{bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end{bmatrix} \] + +<p> + This final equation gives us a formula for calculating the tangent vector \(T\) and bitangent vector \(B\) from a triangle's two edges and its texture coordinates. +</p> + +<p> + Don't worry if you do not fully understand the mathematics behind this. As long as you understand that we can calculate tangents and bitangents from a triangle's vertices and its texture coordinates (since texture coordinates are in the same space as tangent vectors) you're halfway there. +</p> + +<h3>Manual calculation of tangents and bitangents</h3> +<p> + In the previous demo we had a simple normal mapped plane facing the positive z direction. This time we want to implement normal mapping using tangent space so we can orient this plane however we want and normal mapping would still work. Using the previously discussed mathematics we're going to manually calculate this surface's tangent and bitangent vectors. +</p> + +<p> + Let's assume the plane is built up from the following vectors (with 1, 2, 3 and 1, 3, 4 as its two triangles): +</p> + +<pre><code> +// positions +glm::vec3 pos1(-1.0, 1.0, 0.0); +glm::vec3 pos2(-1.0, -1.0, 0.0); +glm::vec3 pos3( 1.0, -1.0, 0.0); +glm::vec3 pos4( 1.0, 1.0, 0.0); +// texture coordinates +glm::vec2 uv1(0.0, 1.0); +glm::vec2 uv2(0.0, 0.0); +glm::vec2 uv3(1.0, 0.0); +glm::vec2 uv4(1.0, 1.0); +// normal vector +glm::vec3 nm(0.0, 0.0, 1.0); +</code></pre> + +<p> + We first calculate the first triangle's edges and delta UV coordinates: +</p> + +<pre><code> +glm::vec3 edge1 = pos2 - pos1; +glm::vec3 edge2 = pos3 - pos1; +glm::vec2 deltaUV1 = uv2 - uv1; +glm::vec2 deltaUV2 = uv3 - uv1; +</code></pre> + +<p> + With the required data for calculating tangents and bitangents we can start following the equation from the previous section: +</p> + +<pre><code> +float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + +tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); +tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); +tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + +bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); +bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); +bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + +[...] // similar procedure for calculating tangent/bitangent for plane's second triangle +</code></pre> + +<p> + Here we first pre-calculate the fractional part of the equation as <var>f</var> and then for each vector component we do the corresponding matrix multiplication multiplied by <var>f</var>. If you compare this code with the final equation you can see it is a direct translation. Because a triangle is always a flat shape, we only need to calculate a single tangent/bitangent pair per triangle as they will be the same for each of the triangle's vertices. +</p> + +<p> + The resulting tangent and bitangent vector should have a value of (<code>1</code>,<code>0</code>,<code>0</code>) and (<code>0</code>,<code>1</code>,<code>0</code>) respectively that together with the normal (<code>0</code>,<code>0</code>,<code>1</code>) forms an orthogonal TBN matrix. Visualized on the plane, the TBN vectors would look like this: +</p> + + <img src="/img/advanced-lighting/normal_mapping_tbn_shown.png" class="clean" alt="Image of TBN vectors visualized on a plane in OpenGL"/> + +<p> + With tangent and bitangent vectors defined per vertex we can start implementing <em>proper</em> normal mapping. +</p> + +<h3>Tangent space normal mapping</h3> +<p> + To get normal mapping working, we first have to create a TBN matrix in the shaders. To do that, we pass the earlier calculated tangent and bitangent vectors to the vertex shader as vertex attributes: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in vec3 aTangent; +layout (location = 4) in vec3 aBitangent; +</code></pre> + +<p> + Then within the vertex shader's <fun>main</fun> function we create the TBN matrix: +</p> + +<pre><code> +void main() +{ + [...] + vec3 T = normalize(vec3(model * vec4(aTangent, 0.0))); + vec3 B = normalize(vec3(model * vec4(aBitangent, 0.0))); + vec3 N = normalize(vec3(model * vec4(aNormal, 0.0))); + mat3 TBN = mat3(T, B, N); +} +</code></pre> + +<p> + Here we first transform all the TBN vectors to the coordinate system we'd like to work in, which in this case is world-space as we multiply them with the <var>model</var> matrix. Then we create the actual TBN matrix by directly supplying <fun>mat3</fun>'s constructor with the relevant column vectors. Note that if we want to be really precise, we would multiply the TBN vectors with the normal matrix as we only care about the orientation of the vectors. +</p> + +<note> + Technically there is no need for the <var>bitangent</var> variable in the vertex shader. All three TBN vectors are perpendicular to each other so we can calculate the <var>bitangent</var> ourselves in the vertex shader by taking the cross product of the <var>T</var> and <var>N</var> vector: <code>vec3 B = cross(N, T);</code> +</note> + +<p> + So now that we have a TBN matrix, how are we going to use it? There are two ways we can use a TBN matrix for normal mapping, and we'll demonstrate both of them: +</p> + +<ol> + <li>We take the TBN matrix that transforms any vector from tangent to world space, give it to the fragment shader, and transform the sampled normal from tangent space to world space using the TBN matrix; the normal is then in the same space as the other lighting variables.</li> + <li>We take the inverse of the TBN matrix that transforms any vector from world space to tangent space, and use this matrix to transform not the normal, but the other relevant lighting variables to tangent space; the normal is then again in the same space as the other lighting variables.</li> +</ol> + +<p> + Let's review the first case. The normal vector we sample from the normal map is expressed in tangent space whereas the other lighting vectors (light and view direction) are expressed in world space. By passing the TBN matrix to the fragment shader we can multiply the sampled tangent space normal with this TBN matrix to transform the normal vector to the same reference space as the other lighting vectors. This way, all the lighting calculations (specifically the dot product) make sense. +</p> + +<p> + Sending the TBN matrix to the fragment shader is easy: +</p> + +<pre><code> +out VS_OUT { + vec3 FragPos; + vec2 TexCoords; + mat3 TBN; +} vs_out; + +void main() +{ + [...] + vs_out.TBN = mat3(T, B, N); +} +</code></pre> + +<p> + In the fragment shader we similarly take a <code>mat3</code> as an input variable: +</p> + +<pre><code> +in VS_OUT { + vec3 FragPos; + vec2 TexCoords; + mat3 TBN; +} fs_in; +</code></pre> + +<p> + With this TBN matrix we can now update the normal mapping code to include the tangent-to-world space transformation: +</p> + +<pre class="cpp"><code> +normal = texture(normalMap, fs_in.TexCoords).rgb; +normal = normal * 2.0 - 1.0; +normal = normalize(fs_in.TBN * normal); +</code></pre> + +<p> + Because the resulting <var>normal</var> is now in world space, there is no need to change any of the other fragment shader code as the lighting code assumes the normal vector to be in world space. +</p> + +<p> + Let's also review the second case, where we take the inverse of the TBN matrix to transform all relevant world-space vectors to the space the sampled normal vectors are in: tangent space. The construction of the TBN matrix remains the same, but we first invert the matrix before sending it to the fragment shader: +</p> + +<pre><code> +vs_out.TBN = transpose(mat3(T, B, N)); +</code></pre> + +<p> + Note that we use the <fun>transpose</fun> function instead of the <fun>inverse</fun> function here. A great property of orthogonal matrices (each axis is a perpendicular unit vector) is that the transpose of an orthogonal matrix equals its inverse. This is a great property as <fun>inverse</fun> is expensive and a transpose isn't. +</p> + +<p> + Within the fragment shader we do not transform the normal vector, but we transform the other relevant vectors to tangent space, namely the <var>lightDir</var> and <var>viewDir</var> vectors. That way, each vector is in the same coordinate space: tangent space. +</p> + +<pre><code> +void main() +{ + vec3 normal = texture(normalMap, fs_in.TexCoords).rgb; + normal = normalize(normal * 2.0 - 1.0); + + vec3 lightDir = fs_in.TBN * normalize(lightPos - fs_in.FragPos); + vec3 viewDir = fs_in.TBN * normalize(viewPos - fs_in.FragPos); + [...] +} +</code></pre> + +<p> + The second approach looks like more work and also requires matrix multiplications in the fragment shader, so why would we bother with the second approach? +</p> + +<p> + Well, transforming vectors from world to tangent space has an added advantage in that we can transform all the relevant lighting vectors to tangent space in the vertex shader instead of in the fragment shader. This works, because <var>lightPos</var> and <var>viewPos</var> don't update every fragment run, and for <var>fs_in.FragPos</var> we can calculate its tangent-space position in the vertex shader and let fragment interpolation do its work. There is effectively no need to transform a vector to tangent space in the fragment shader, while it is necessary with the first approach as sampled normal vectors are specific to each fragment shader run. +</p> + +<p> + So instead of sending the inverse of the TBN matrix to the fragment shader, we send a tangent-space light position, view position, and vertex position to the fragment shader. This saves us from having to do matrix multiplications in the fragment shader. This is a nice optimization as the vertex shader runs considerably less often than the fragment shader. This is also the reason why this approach is often the preferred approach. +</p> + +<pre><code> +out VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} vs_out; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +[...] + +void main() +{ + [...] + mat3 TBN = transpose(mat3(T, B, N)); + vs_out.TangentLightPos = TBN * lightPos; + vs_out.TangentViewPos = TBN * viewPos; + vs_out.TangentFragPos = TBN * vec3(model * vec4(aPos, 1.0)); +} +</code></pre> + +<p> + In the fragment shader we then use these new input variables to calculate lighting in tangent space. As the normal vector is already in tangent space, the lighting makes sense. +</p> + +<p> + With normal mapping applied in tangent space, we should get similar results to what we had at the start of this chapter. This time however, we can orient our plane in any way we'd like and the lighting would still be correct: +</p> + +<pre><code> +glm::mat4 model = glm::mat4(1.0f); +model = <function id='57'>glm::rotate</function>(model, (float)<function id='47'>glfwGetTime</function>() * -10.0f, glm::normalize(glm::vec3(1.0, 0.0, 1.0))); +shader.setMat4("model", model); +RenderQuad(); +</code></pre> + +<p> + Which indeed looks like proper normal mapping: +</p> + + <img src="/img/advanced-lighting/normal_mapping_correct_tangent.png" class="clean" alt="Correct normal mapping with tangent space transformations in OpenGL"/> + +<p> + You can find the source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/4.normal_mapping/normal_mapping.cpp" target="_blank">here</a>. +</p> + +<h2>Complex objects</h2> +<p> + We've demonstrated how we can use normal mapping, together with tangent space transformations, by manually calculating the tangent and bitangent vectors. Luckily for us, having to manually calculate these tangent and bitangent vectors is not something we do too often. Most of the time you implement it once in a custom model loader, or in our case use a <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">model loader</a> using Assimp. +</p> + +<p> + Assimp has a very useful configuration bit we can set when loading a model called <var>aiProcess_CalcTangentSpace</var>. When the <var>aiProcess_CalcTangentSpace</var> bit is supplied to Assimp's <fun>ReadFile</fun> function, Assimp calculates smooth tangent and bitangent vectors for each of the loaded vertices, similarly to how we did it in this chapter. +</p> + +<pre><code> +const aiScene *scene = importer.ReadFile( + path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace +); +</code></pre> + +<p> + Within Assimp we can then retrieve the calculated tangents via: +</p> + +<pre><code> +vector.x = mesh->mTangents[i].x; +vector.y = mesh->mTangents[i].y; +vector.z = mesh->mTangents[i].z; +vertex.Tangent = vector; +</code></pre> + +<p> + Then you'll have to update the model loader to also load normal maps from a textured model. The wavefront object format (.obj) exports normal maps slightly different from Assimp's conventions as <var>aiTextureType_NORMAL</var> doesn't load normal maps, while <var>aiTextureType_HEIGHT</var> does: +</p> + +<pre><code> +vector&lt;Texture&gt; normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); +</code></pre> + +<p> + Of course, this is different for each type of loaded model and file format. +</p> + + <!--It's also important to realize that <var>aiProcess_CalcTangentSpace</var> doesn't always work. Calculating tangents is based on texture coordinates and some 3D artists do certain texture tricks like mirroring a texture surface over a model; this gives incorrect results when the mirroring is not taken into account. The nanosuit model for instance doesn't produce proper tangents as it has mirrored texture coordinates. Assimp gives us a multiplication factor (<code>1</code> or <code>-1</code>) in the tangent's <code>w</code> coordinate that we can use to multiply the bitangent with to account for the mirroring. +</p>--> + +<p> + Running the application on a model with specular and normal maps, using an updated model loader, gives the following result: +</p> + + <img src="/img/advanced-lighting/normal_mapping_complex_compare.png" alt="Normal mapping in OpenGL on a complex object loaded with Assimp"/> + +<p> + As you can see, normal mapping boosts the detail of an object by an incredible amount without too much extra cost. +</p> + +<p> + Using normal maps is also a great way to boost performance. Before normal mapping, you had to use a large number of vertices to get a high number of detail on a mesh. With normal mapping, we can get the same level of detail on a mesh using a lot less vertices. The image below from Paolo Cignoni shows a nice comparison of both methods: +</p> + + <img src="/img/advanced-lighting/normal_mapping_comparison.png" alt="Comparrison of visualizing details on a mesh with and without normal mapping"/> + +<p> + The details on both the high-vertex mesh and the low-vertex mesh with normal mapping are almost indistinguishable. So normal mapping doesn't only look nice, it's a great tool to replace high-vertex meshes with low-vertex meshes without losing (too much) detail. +</p> + +<h2>One last thing</h2> +<p> + There is one last trick left to discuss that slightly improves quality without too much extra cost. +</p> + +<p> + When tangent vectors are calculated on larger meshes that share a considerable amount of vertices, the tangent vectors are generally averaged to give nice and smooth results. A problem with this approach is that the three TBN vectors could end up non-perpendicular, which means the resulting TBN matrix would no longer be orthogonal. Normal mapping would only be slightly off with a non-orthogonal TBN matrix, but it's still something we can improve. +</p> + +<p> + Using a mathematical trick called the <def>Gram-Schmidt process</def>, we can <def>re-orthogonalize</def> the TBN vectors such that each vector is again perpendicular to the other vectors. Within the vertex shader we would do it like this: +</p> + +<pre><code> +vec3 T = normalize(vec3(model * vec4(aTangent, 0.0))); +vec3 N = normalize(vec3(model * vec4(aNormal, 0.0))); +// re-orthogonalize T with respect to N +T = normalize(T - dot(T, N) * N); +// then retrieve perpendicular vector B with the cross product of T and N +vec3 B = cross(N, T); + +mat3 TBN = mat3(T, B, N) +</code></pre> + +<p> + This, albeit by a little, generally improves the normal mapping results with a little extra cost. Take a look at the end of the <em>Normal Mapping Mathematics</em> video in the additional resources for a great explanation of how this process actually works. +</p> + +<h2>Additional resources</h2> + <ul> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial26/tutorial26.html" target="_blank">Tutorial 26: Normal Mapping</a>: normal mapping tutorial by ogldev.</li> + <li><a href="https://www.youtube.com/watch?v=LIOPYmknj5Q" target="_blank">How Normal Mapping Works</a>: a nice video tutorial of how normal mapping works by TheBennyBox.</li> + <li><a href="https://www.youtube.com/watch?v=4FaWLgsctqY" target="_blank">Normal Mapping Mathematics</a>: a similar video by TheBennyBox about the mathematics behind normal mapping.</li> + <li><a href="http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/" target="_blank">Tutorial 13: Normal Mapping</a>: normal mapping tutorial by opengl-tutorial.org.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Parallax-Mapping.html b/pub/Advanced-Lighting/Parallax-Mapping.html @@ -0,0 +1,729 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Parallax Mapping</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Parallax-Mapping</h1> +<p> + Parallax mapping is a technique similar to normal mapping, but based on different principles. Just like normal mapping it is a technique that significantly boosts a textured surface's detail and gives it a sense of depth. While also an illusion, parallax mapping is a lot better in conveying a sense of depth and together with normal mapping gives incredibly realistic results. While parallax mapping isn't necessarily a technique directly related to (advanced) lighting, I'll still discuss it here as the technique is a logical follow-up of normal mapping. Note that getting an understanding of normal mapping, specifically tangent space, is strongly advised before learning parallax mapping. +</p> + +<p> + Parallax mapping is closely related to the family of <def>displacement mapping</def> techniques that <em>displace</em> or <em>offset</em> vertices based on geometrical information stored inside a texture. One way to do this, is to take a plane with roughly 1000 vertices and displace each of these vertices based on a value in a texture that tells us the height of the plane at that specific area. Such a texture that contains height values per texel is called a <def>height map</def>. An example height map derived from the geometric properties of a simple brick surface looks a bit like this: +</p> + +<img src="/img/advanced-lighting/parallax_mapping_height_map.png" alt="Height map used in OpenGL for parallax mapping"/> + +<p> + When spanned over a plane, each vertex is displaced based on the sampled height value in the height map, transforming a flat plane to a rough bumpy surface based on a material's geometric properties. For instance, taking a flat plane displaced with the above heightmap results in the following image: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_plane_heightmap.png" class="clean" alt="Height map applied to simple plane"/> + +<p> + A problem with displacing vertices this way is that a plane needs to contain a huge amount of triangles to get a realistic displacement, otherwise the displacement looks too blocky. As each flat surface may then require over 10000 vertices this quickly becomes computationally infeasible. What if we could somehow achieve similar realism without the need of extra vertices? In fact, what if I were to tell you that the previously shown displaced surface is actually rendered with only 2 triangles. This brick surface shown is rendered with <def>parallax mapping</def>, a displacement mapping technique that doesn't require extra vertex data to convey depth, but (similar to normal mapping) uses a clever technique to trick the user. +</p> + +<p> + The idea behind parallax mapping is to alter the texture coordinates in such a way that it looks like a fragment's surface is higher or lower than it actually is, all based on the view direction and a heightmap. To understand how it works, take a look at the following image of our brick surface: + </p> + + <img src="/img/advanced-lighting/parallax_mapping_plane_height.png" class="clean" alt="Diagram of how parallax mapping works in OpenGL"/> + +<p> + Here the rough red line represents the values in the heightmap as the geometric surface representation of the brick surface and the vector \(\color{orange}{\bar{V}}\) represents the surface to view direction (<var>viewDir</var>). If the plane would have actual displacement, the viewer would see the surface at point \(\color{blue}B\). However, as our plane has no actual displacement the view direction is calculated from point \(\color{green}A\) as we'd expect. Parallax mapping aims to offset the texture coordinates at fragment position \(\color{green}A\) in such a way that we get texture coordinates at point \(\color{blue}B\). We then use the texture coordinates at point \(\color{blue}B\) for all subsequent texture samples, making it look like the viewer is actually looking at point \(\color{blue}B\). +</p> + +<p> + The trick is to figure out how to get the texture coordinates at point \(\color{blue}B\) from point \(\color{green}A\). Parallax mapping tries to solve this by scaling the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) by the height at fragment \(\color{green}A\). So we're scaling the length of \(\color{orange}{\bar{V}}\) to be equal to a sampled value from the heightmap \(\color{green}{H(A)}\) at fragment position \(\color{green}A\). The image below shows this scaled vector \(\color{brown}{\bar{P}}\): +</p> + + <img src="/img/advanced-lighting/parallax_mapping_scaled_height.png" class="clean" alt="Diagram of how parallax mapping works in OpenGL with vector scaled by fragment's height."/> + +<p> + We then take this vector \(\color{brown}{\bar{P}}\) and take its vector coordinates that align with the plane as the texture coordinate offset. This works because vector \(\color{brown}{\bar{P}}\) is calculated using a height value from the heightmap. So the higher a fragment's height, the more it effectively gets displaced. +</p> + +<p> + This little trick gives good results most of the time, but it is still a really crude approximation to get to point \(\color{blue}B\). When heights change rapidly over a surface the results tend to look unrealistic as the vector \(\color{brown}{\bar{P}}\) will not end up close to \(\color{blue}B\) as you can see below: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_incorrect_p.png" class="clean" alt="Diagram of why basic parallax mapping gives incorrect result at steep height changes."/> + +<p> + Another issue with parallax mapping is that it's difficult to figure out which coordinates to retrieve from \(\color{brown}{\bar{P}}\) when the surface is arbitrarily rotated in some way. We'd rather do this in a different coordinate space where the <code>x</code> and <code>y</code> component of vector \(\color{brown}{\bar{P}}\) always align with the texture's surface. If you've followed along in the <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" target="_blank">normal mapping</a> chapter you probably guessed how we can accomplish this. And yes, we would like to do parallax mapping in tangent space. +</p> + +<p> + By transforming the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) to tangent space, the transformed \(\color{brown}{\bar{P}}\) vector will have its <code>x</code> and <code>y</code> component aligned to the surface's tangent and bitangent vectors. As the tangent and bitangent vectors are pointing in the same direction as the surface's texture coordinates we can take the <code>x</code> and <code>y</code> components of \(\color{brown}{\bar{P}}\) as the texture coordinate offset, regardless of the surface's orientation. +</p> + +<p> + But enough about the theory, let's get our feet wet and start implementing actual parallax mapping. +</p> + +<h2>Parallax mapping</h2> +<p> + For parallax mapping we're going to use a simple 2D plane for which we calculated its tangent and bitangent vectors before sending it to the GPU; similar to what we did in the normal mapping chapter. Onto the plane we're going to attach a <a href="/img/textures/bricks2.jpg" target="_blank">diffuse texture</a>, a <a href="/img/textures/bricks2_normal.jpg" target="_blank">normal map</a>, and a <a href="/img/textures/bricks2_disp.jpg" target="_blank">displacement map</a> that you can download from their urls. For this example we're going to use parallax mapping in conjunction with normal mapping. Because parallax mapping gives the illusion of displacing a surface, the illusion breaks when the lighting doesn't match. As normal maps are often generated from heightmaps, using a normal map together with the heightmap makes sure the lighting is in place with the displacement. +</p> + +<p> + You may have already noted that the displacement map linked above is the inverse of the heightmap shown at the start of this chapter. With parallax mapping it makes more sense to use the inverse of the heightmap as it's easier to fake depth than height on flat surfaces. This slightly changes how we perceive parallax mapping as shown below: +</p> + +<img src="/img/advanced-lighting/parallax_mapping_depth.png" class="clean" alt="Parallax mapping using a depth map instead of a heightmap"/> + +<p> + We again have a points \(\color{green}A\) and \(\color{blue}B\), but this time we obtain vector \(\color{brown}{\bar{P}}\) by <strong>subtracting</strong> vector \(\color{orange}{\bar{V}}\) from the texture coordinates at point \(\color{green}A\). We can obtain depth values instead of height values by subtracting the sampled heightmap values from <code>1.0</code> in the shaders, or by simply inversing its texture values in image-editing software as we did with the depthmap linked above. +</p> + + +<p> + Parallax mapping is implemented in the fragment shader as the displacement effect is different all over a triangle's surface. In the fragment shader we're then going to need to calculate the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) so we need the view position and a fragment position in tangent space. In the normal mapping chapter we already had a vertex shader that sends these vectors in tangent space so we can take an exact copy of that chapter's vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in vec3 aTangent; +layout (location = 4) in vec3 aBitangent; + +out VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} vs_out; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); + vs_out.TexCoords = aTexCoords; + + vec3 T = normalize(mat3(model) * aTangent); + vec3 B = normalize(mat3(model) * aBitangent); + vec3 N = normalize(mat3(model) * aNormal); + mat3 TBN = transpose(mat3(T, B, N)); + + vs_out.TangentLightPos = TBN * lightPos; + vs_out.TangentViewPos = TBN * viewPos; + vs_out.TangentFragPos = TBN * vs_out.FragPos; +} +</code></pre> + +<p> + Within the fragment shader we then implement the parallax mapping logic. The fragment shader looks a bit like this: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} fs_in; + +uniform sampler2D diffuseMap; +uniform sampler2D normalMap; +uniform sampler2D depthMap; + +uniform float height_scale; + +vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir); + +void main() +{ + // offset texture coordinates with Parallax Mapping + vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); + vec2 texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); + + // then sample textures with new texture coords + vec3 diffuse = texture(diffuseMap, texCoords); + vec3 normal = texture(normalMap, texCoords); + normal = normalize(normal * 2.0 - 1.0); + // proceed with lighting code + [...] +} + +</code></pre> + +<p> + We defined a function called <fun>ParallaxMapping</fun> that takes as input the fragment's texture coordinates and the fragment-to-view direction \(\color{orange}{\bar{V}}\) in tangent space. The function returns the displaced texture coordinates. We then use these <em>displaced</em> texture coordinates as the texture coordinates for sampling the diffuse and normal map. As a result, the fragment's diffuse and normal vector correctly corresponds to the surface's displaced geometry. +</p> + +<p> + Let's take a look inside the <fun>ParallaxMapping</fun> function: +</p> + +<pre><code> +vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) +{ + float height = texture(depthMap, texCoords).r; + vec2 p = viewDir.xy / viewDir.z * (height * height_scale); + return texCoords - p; +} +</code></pre> + +<p> + This relatively simple function is a direct translation of what we've discussed so far. We take the original texture coordinates <var>texCoords</var> and use these to sample the height (or depth) from the <var>depthMap</var> at the current fragment \(\color{green}{A}\) as \(\color{green}{H(A)}\). We then calculate \(\color{brown}{\bar{P}}\) as the <code>x</code> and <code>y</code> component of the tangent-space <var>viewDir</var> vector divided by its <code>z</code> component and scaled by \(\color{green}{H(A)}\). We also introduced a <var>height_scale</var> uniform for some extra control as the parallax effect is usually too strong without an extra scale parameter. We then subtract this vector \(\color{brown}{\bar{P}}\) from the texture coordinates to get the final displaced texture coordinates. + </p> + +<p> + What is interesting to note here is the division of <var>viewDir.xy</var> by <var>viewDir.z</var>. As the <var>viewDir</var> vector is normalized, <var>viewDir.z</var> will be somewhere in the range between <code>0.0</code> and <code>1.0</code>. When <var>viewDir</var> is largely parallel to the surface, its <code>z</code> component is close to <code>0.0</code> and the division returns a much larger vector \(\color{brown}{\bar{P}}\) compared to when <var>viewDir</var> is largely perpendicular to the surface. We're adjusting the size of \(\color{brown}{\bar{P}}\) in such a way that it offsets the texture coordinates at a larger scale when looking at a surface from an angle compared to when looking at it from the top; this gives more realistic results at angles. <br/> + Some prefer to leave the division by <var>viewDir.z</var> out of the equation as default Parallax Mapping could produce undesirable results at angles; the technique is then called <def>Parallax Mapping with Offset Limiting</def>. Choosing which technique to pick is usually a matter of personal preference. +</p> + +<p> + The resulting texture coordinates are then used to sample the other textures (diffuse and normal) and this gives a very neat displaced effect as you can see below with a <var>height_scale</var> of roughly <code>0.1</code>: +</p> + +<img src="/img/advanced-lighting/parallax_mapping.png" alt="Image of parallax mapping in OpenGL"/> + +<p> + Here you can see the difference between normal mapping and parallax mapping combined with normal mapping. Because parallax mapping tries to simulate depth it is actually possible to have bricks overlap other bricks based on the direction you view them. +</p> + +<p> + You can still see a few weird border artifacts at the edge of the parallax mapped plane. This happens because at the edges of the plane the displaced texture coordinates can oversample outside the range [<code>0</code>, <code>1</code>]. This gives unrealistic results based on the texture's wrapping mode(s). A cool trick to solve this issue is to discard the fragment whenever it samples outside the default texture coordinate range: +</p> + +<pre><code> +texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); +if(texCoords.x &gt; 1.0 || texCoords.y &gt; 1.0 || texCoords.x &lt; 0.0 || texCoords.y &lt; 0.0) + discard; +</code></pre> + +<p> + All fragments with (displaced) texture coordinates outside the default range are discarded and Parallax Mapping then gives proper result around the edges of a surface. Note that this trick doesn't work on all types of surfaces, but when applied to a plane it gives great results: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_edge_fix.png" class="clean" alt="Parallax mapping with fragments discarded at the borders, fixing edge artifacts in OpenGL"/> + +<p> + You can find the source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.1.parallax_mapping/parallax_mapping.cpp" target="_blank">here</a>. +</p> + +<p> + It looks great and is quite fast as well as we only need a single extra texture sample for parallax mapping to work. It does come with a few issues though as it sort of breaks down when looking at it from an angle (similar to normal mapping) and gives incorrect results with steep height changes, as you can see below: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_issues.png" alt="Three images displaying the issues with standard parallax mapping: breaks down at angles and incorrect results with steep height changes."/> + +<p> + The reason that it doesn't work properly at times is that it's just a crude approximation of displacement mapping. There are some extra tricks however that still allows us to get almost perfect results with steep height changes, even when looking at an angle. For instance, what if we instead of one sample take multiple samples to find the closest point to \(\color{blue}B\)? +</p> + +<h2>Steep Parallax Mapping</h2> +<p> + Steep Parallax Mapping is an extension on top of Parallax Mapping in that it uses the same principles, but instead of 1 sample it takes multiple samples to better pinpoint vector \(\color{brown}{\bar{P}}\) to \(\color{blue}B\). This gives much better results, even with steep height changes, as the accuracy of the technique is improved by the number of samples. +</p> + +<p> + The general idea of Steep Parallax Mapping is that it divides the total depth range into multiple layers of the same height/depth. For each of these layers we sample the depthmap, shifting the texture coordinates along the direction of \(\color{brown}{\bar{P}}\), until we find a sampled depth value that is less than the depth value of the current layer. Take a look at the following image: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_steep_parallax_mapping_diagram.png" class="clean" alt="Diagram of how steep Parallax Mapping works in OpenGL"/> + +<p> + We traverse the depth layers from the top down and for each layer we compare its depth value to the depth value stored in the depthmap. If the layer's depth value is less than the depthmap's value it means this layer's part of vector \(\color{brown}{\bar{P}}\) is not below the surface. We continue this process until the layer's depth is higher than the value stored in the depthmap: this point is then below the (displaced) geometric surface. +</p> + +<p> + In this example we can see that the depthmap value at the second layer (D(2) = 0.73) is lower than the second layer's depth value <code>0.4</code> so we continue. In the next iteration, the layer's depth value <code>0.6</code> is higher than the depthmap's sampled depth value (D(3) = 0.37). We can thus assume vector \(\color{brown}{\bar{P}}\) at the third layer to be the most viable position of the displaced geometry. We then take the texture coordinate offset \(T_3\) from vector \(\color{brown}{\bar{P_3}}\) to displace the fragment's texture coordinates. You can see how the accuracy increases with more depth layers. +</p> + +<p> + To implement this technique we only have to change the <fun>ParallaxMapping</fun> function as we already have all the variables we need: +</p> + +<pre><code> +vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) +{ + // number of depth layers + const float numLayers = 10; + // calculate the size of each layer + float layerDepth = 1.0 / numLayers; + // depth of current layer + float currentLayerDepth = 0.0; + // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy * height_scale; + vec2 deltaTexCoords = P / numLayers; + + [...] +} +</code></pre> + +<p> + Here we first set things up: we specify the number of layers, calculate the depth offset of each layer, and finally calculate the texture coordinate offset that we have to shift along the direction of \(\color{brown}{\bar{P}}\) per layer. +</p> + +<p> + We then iterate through all the layers, starting from the top, until we find a depthmap value less than the layer's depth value: +</p> + +<pre><code> +// get initial values +vec2 currentTexCoords = texCoords; +float currentDepthMapValue = texture(depthMap, currentTexCoords).r; + +while(currentLayerDepth &lt; currentDepthMapValue) +{ + // shift texture coordinates along direction of P + currentTexCoords -= deltaTexCoords; + // get depthmap value at current texture coordinates + currentDepthMapValue = texture(depthMap, currentTexCoords).r; + // get depth of next layer + currentLayerDepth += layerDepth; +} + +return currentTexCoords; +</code></pre> + +<p> + Here we loop over each depth layer and stop until we find the texture coordinate offset along vector \(\color{brown}{\bar{P}}\) that first returns a depth that's below the (displaced) surface. The resulting offset is subtracted from the fragment's texture coordinates to get a final displaced texture coordinate vector, this time with much more accuracy compared to traditional parallax mapping. +</p> + +<p> + With around <code>10</code> samples the brick surface already looks more viable even when looking at it from an angle, but steep parallax mapping really shines when having a complex surface with steep height changes; like the earlier displayed wooden toy surface: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_steep_parallax_mapping.png" class="clean" alt="Steep Parallax Mapping implemented in OpenGL"/> + +<p> + We can improve the algorithm a bit by exploiting one of Parallax Mapping's properties. When looking straight onto a surface there isn't much texture displacement going on while there is a lot of displacement when looking at a surface from an angle (visualize the view direction on both cases). By taking less samples when looking straight at a surface and more samples when looking at an angle we only sample the necessary amount: +</p> + +<pre><code> +const float minLayers = 8.0; +const float maxLayers = 32.0; +float numLayers = mix(maxLayers, minLayers, max(dot(vec3(0.0, 0.0, 1.0), viewDir), 0.0)); +</code></pre> + +<p> + Here we take the dot product of <var>viewDir</var> and the positive z direction and use its result to align the number of samples to <var>minLayers</var> or <var>maxLayers</var> based on the angle we're looking towards a surface (note that the positive z direction equals the surface's normal vector in tangent space). If we were to look at a direction parallel to the surface we'd use a total of <code>32</code> layers. +</p> + +<p> + You can find the updated source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.2.steep_parallax_mapping/steep_parallax_mapping.cpp" target="_blank">here</a>. You can also find the wooden toy box surface here: <a href="/img/textures/wood.png" target="_blank">diffuse</a>, <a href="/img/textures/toy_box_normal.png" target="_blank">normal</a> and <a href="/img/textures/toy_box_disp.png" target="_blank">depth</a>. +</p> + +<p> + Steep Parallax Mapping also comes with its problems though. Because the technique is based on a finite number of samples, we get aliasing effects and the clear distinctions between layers can easily be spotted: +</p> + + <img src="/img/advanced-lighting/parallax_mapping_steep_artifact.png" class="clean" alt="The visible layers of Steep Parallax Mapping can easily be detected with small numbers"/> + +<p> + We can reduce the issue by taking a larger number of samples, but this quickly becomes too heavy a burden on performance. There are several approaches that aim to fix this issue by not taking the first position that's below the (displaced) surface, but by <em>interpolating</em> between the position's two closest depth layers to find a much closer match to \(\color{blue}B\). +</p> + +<p> + Two of the more popular of these approaches are called <def>Relief Parallax Mapping</def> and <def>Parallax Occlusion Mapping</def> of which Relief Parallax Mapping gives the most accurate results, but is also more performance heavy compared to Parallax Occlusion Mapping. Because Parallax Occlusion Mapping gives almost the same results as Relief Parallax Mapping and is also more efficient it is often the preferred approach. +</p> + +<h2>Parallax Occlusion Mapping</h2> +<p> + Parallax Occlusion Mapping is based on the same principles as Steep Parallax Mapping, but instead of taking the texture coordinates of the first depth layer after a collision, we're going to linearly interpolate between the depth layer after and before the collision. We base the weight of the linear interpolation on how far the surface's height is from the depth layer's value of both layers. Take a look at the following picture to get a grasp of how it works: +</p> + +<img src="/img/advanced-lighting/parallax_mapping_parallax_occlusion_mapping_diagram.png" class="clean" alt="How Parallax Occlusion Mapping works in OpenGL"/> + +<p> + As you can see, it's largely similar to Steep Parallax Mapping with as an extra step the linear interpolation between the two depth layers' texture coordinates surrounding the intersected point. This is again an approximation, but significantly more accurate than Steep Parallax Mapping. +</p> + +<p> + The code for Parallax Occlusion Mapping is an extension on top of Steep Parallax Mapping and not too difficult: +</p> + +<pre><code> +[...] // steep parallax mapping code here + +// get texture coordinates before collision (reverse operations) +vec2 prevTexCoords = currentTexCoords + deltaTexCoords; + +// get depth after and before collision for linear interpolation +float afterDepth = currentDepthMapValue - currentLayerDepth; +float beforeDepth = texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth; + +// interpolation of texture coordinates +float weight = afterDepth / (afterDepth - beforeDepth); +vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); + +return finalTexCoords; +</code></pre> + +<p> + After we found the depth layer after intersecting the (displaced) surface geometry, we also retrieve the texture coordinates of the depth layer before intersection. Then we calculate the distance of the (displaced) geometry's depth from the corresponding depth layers and interpolate between these two values. The linear interpolation is a basic interpolation between both layer's texture coordinates. The function then finally returns the final interpolated texture coordinates. +</p> + +<p> + Parallax Occlusion Mapping gives surprisingly good results and although some slight artifacts and aliasing issues are still visible, it's a generally a good trade-off and only really visible when heavily zoomed in or looking at very steep angles. +</p> + + <img src="/img/advanced-lighting/parallax_mapping_parallax_occlusion_mapping.png" alt="Image of Parallax Occlusion Mapping in OpenGL"/> + +<p> + You can find the source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.3.parallax_occlusion_mapping/parallax_occlusion_mapping.cpp" target="_blank">here</a>. +</p> + +<p> + Parallax Mapping is a great technique to boost the detail of your scene, but does come with a few artifacts you'll have to consider when using it. Most often, parallax mapping is used on floor or wall-like surfaces where it's not as easy to determine the surface's outline and the viewing angle is most often roughly perpendicular to the surface. This way, the artifacts of Parallax Mapping aren't as noticeable and make it an incredibly interesting technique for boosting your objects' details. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://sunandblackcat.com/tipFullView.php?topicid=28" target="_blank">Parallax Occlusion Mapping in GLSL</a>: great parallax mapping tutorial by sunandblackcat.com.</li> + <li><a href="https://www.youtube.com/watch?v=xvOT62L-fQI" target="_blank">How Parallax Displacement Mapping Works</a>: a nice video tutorial of how parallax mapping works by TheBennyBox.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/SSAO.html b/pub/Advanced-Lighting/SSAO.html @@ -0,0 +1,954 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">SSAO</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/SSAO</h1> +<p> + We've briefly touched the topic in the basic lighting chapter: ambient lighting. Ambient lighting is a fixed light constant we add to the overall lighting of a scene to simulate the <def>scattering</def> of light. In reality, light scatters in all kinds of directions with varying intensities so the indirectly lit parts of a scene should also have varying intensities. One type of indirect lighting approximation is called <def>ambient occlusion</def> that tries to approximate indirect lighting by darkening creases, holes, and surfaces that are close to each other. These areas are largely occluded by surrounding geometry and thus light rays have fewer places to escape to, hence the areas appear darker. Take a look at the corners and creases of your room to see that the light there seems just a little darker. +</p> + +<p> + Below is an example image of a scene with and without ambient occlusion. Notice how especially between the creases, the (ambient) light is more occluded: +</p> + +<img src="/img/advanced-lighting/ssao_example.png" alt="Example image of SSAO with and without"/> + +<p> + While not an incredibly obvious effect, the image with ambient occlusion enabled does feel a lot more realistic due to these small occlusion-like details, giving the entire scene a greater feel of depth. +</p> + +<p> + Ambient occlusion techniques are expensive as they have to take surrounding geometry into account. One could shoot a large number of rays for each point in space to determine its amount of occlusion, but that quickly becomes computationally infeasible for real-time solutions. In 2007, Crytek published a technique called <def>screen-space ambient occlusion</def> (SSAO) for use in their title <em>Crysis</em>. The technique uses a scene's depth buffer in screen-space to determine the amount of occlusion instead of real geometrical data. This approach is incredibly fast compared to real ambient occlusion and gives plausible results, making it the de-facto standard for approximating real-time ambient occlusion. +</p> + +<p> + The basics behind screen-space ambient occlusion are simple: for each fragment on a screen-filled quad we calculate an <def>occlusion factor</def> based on the fragment's surrounding depth values. The occlusion factor is then used to reduce or nullify the fragment's ambient lighting component. The occlusion factor is obtained by taking multiple depth samples in a sphere sample kernel surrounding the fragment position and compare each of the samples with the current fragment's depth value. The number of samples that have a higher depth value than the fragment's depth represents the occlusion factor. +</p> + +<img src="/img/advanced-lighting/ssao_crysis_circle.png" class="clean" alt="Image of circle based SSAO technique as done by Crysis"/> + +<p> + Each of the gray depth samples that are inside geometry contribute to the total occlusion factor; the more samples we find inside geometry, the less ambient lighting the fragment should eventually receive. +</p> + +<p> + It is clear the quality and precision of the effect directly relates to the number of surrounding samples we take. If the sample count is too low, the precision drastically reduces and we get an artifact called <def>banding</def>; if it is too high, we lose performance. We can reduce the amount of samples we have to test by introducing some randomness into the sample kernel. By randomly rotating the sample kernel each fragment we can get high quality results with a much smaller amount of samples. This does come at a price as the randomness introduces a noticeable <def>noise pattern</def> that we'll have to fix by blurring the results. Below is an image (courtesy of <a href="http://john-chapman-graphics.blogspot.com/" target="_blank">John Chapman</a>) showcasing the banding effect and the effect randomness has on the results: +</p> + +<img src="/img/advanced-lighting/ssao_banding_noise.jpg" alt="The SSAO image quality with multiple samples and a blur added"/> + +<p> + As you can see, even though we get noticeable banding on the SSAO results due to a low sample count, by introducing some randomness the banding effects are completely gone. +</p> + +<p> + The SSAO method developed by Crytek had a certain visual style. Because the sample kernel used was a sphere, it caused flat walls to look gray as half of the kernel samples end up being in the surrounding geometry. Below is an image of Crysis's screen-space ambient occlusion that clearly portrays this gray feel: +</p> + +<img src="/img/advanced-lighting/ssao_crysis.jpg" alt="Screen space ambient occlusion in the Crysis game by Crytek showing a gray feel due to them using a sphere kernel instead of a normal oriented hemisphere sample kernel in OpenGL"/> + +<p> + For that reason we won't be using a sphere sample kernel, but rather a hemisphere sample kernel oriented along a surface's normal vector. +</p> + + <img src="/img/advanced-lighting/ssao_hemisphere.png" class="clean" alt="Image of normal oriented hemisphere sample kernel for SSAO in OpenGL"/> + + <p> + By sampling around this <def>normal-oriented hemisphere</def> we do not consider the fragment's underlying geometry to be a contribution to the occlusion factor. This removes the gray-feel of ambient occlusion and generally produces more realistic results. + This chapter's technique is based on this normal-oriented hemisphere method and a slightly modified version of John Chapman's brilliant <a href="http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html" target="_blank">SSAO tutorial</a>. + </p> + +<h2>Sample buffers</h2> +<p> + SSAO requires geometrical info as we need some way to determine the occlusion factor of a fragment. For each fragment, we're going to need the following data: +</p> + +<ul> + <li>A per-fragment <strong>position</strong> vector.</li> + <li>A per-fragment <strong>normal</strong> vector.</li> + <li>A per-fragment <strong>albedo</strong> color.</li> + <li>A <strong>sample kernel</strong>.</li> + <li>A per-fragment <strong>random rotation</strong> vector used to rotate the sample kernel.</li> +</ul> + +<p> + Using a per-fragment view-space position we can orient a sample hemisphere kernel around the fragment's view-space surface normal and use this kernel to sample the position buffer texture at varying offsets. For each per-fragment kernel sample we compare its depth with its depth in the position buffer to determine the amount of occlusion. The resulting occlusion factor is then used to limit the final ambient lighting component. By also including a per-fragment rotation vector we can significantly reduce the number of samples we'll need to take as we'll soon see. +</p> + + <img src="/img/advanced-lighting/ssao_overview.png" class="clean" alt="An overview of the SSAO screen-space OpenGL technique"/> + +<p> + As SSAO is a screen-space technique we calculate its effect on each fragment on a screen-filled 2D quad. This does mean we have no geometrical information of the scene. What we could do, is render the geometrical per-fragment data into screen-space textures that we then later send to the SSAO shader so we have access to the per-fragment geometrical data. If you've followed along with the previous chapter you'll realize this looks quite like a deferred renderer's G-buffer setup. For that reason SSAO is perfectly suited in combination with deferred rendering as we already have the position and normal vectors in the G-buffer. +</p> + +<note> + In this chapter we're going to implement SSAO on top of a slightly simplified version of the deferred renderer from the <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading" target="_blank">deferred shading</a> chapter. If you're not sure what deferred shading is, be sure to first read up on that. + </note> + +<p> + As we should have per-fragment position and normal data available from the scene objects, the fragment shader of the geometry stage is fairly simple: +</p> + +<pre><code> +#version 330 core +layout (location = 0) out vec4 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; + +in vec2 TexCoords; +in vec3 FragPos; +in vec3 Normal; + +void main() +{ + // store the fragment position vector in the first gbuffer texture + gPosition = FragPos; + // also store the per-fragment normals into the gbuffer + gNormal = normalize(Normal); + // and the diffuse per-fragment color, ignore specular + gAlbedoSpec.rgb = vec3(0.95); +} +</code></pre> + +<p> + Since SSAO is a screen-space technique where occlusion is calculated from the visible view, it makes sense to implement the algorithm in view-space. Therefore, <var>FragPos</var> and <var>Normal</var> as supplied by the geometry stage's vertex shader are transformed to view space (multiplied by the view matrix as well). +</p> + +<note> + It is possible to reconstruct the position vectors from depth values alone, using some clever tricks as Matt Pettineo described in his <a href="https://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/" target="_blank">blog</a>. This requires a few extra calculations in the shaders, but saves us from having to store position data in the G-buffer (which costs a lot of memory). For the sake of a more simple example, we'll leave these optimizations out of the chapter. +</note> + +<p> + The <var>gPosition</var> color buffer texture is configured as follows: +</p> + +<pre><code> +<function id='50'>glGenTextures</function>(1, &gPosition); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gPosition); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +</code></pre> + +<p> + This gives us a position texture that we can use to obtain depth values for each of the kernel samples. Note that we store the positions in a floating point data format; this way position values aren't clamped to [<code>0.0</code>,<code>1.0</code>] and we need the higher precision. Also note the texture wrapping method of <var>GL_CLAMP_TO_EDGE</var>. This ensures we don't accidentally oversample position/depth values in screen-space outside the texture's default coordinate region. +</p> + +<p> + Next, we need the actual hemisphere sample kernel and some method to randomly rotate it. +</p> + +<h2>Normal-oriented hemisphere</h2> +<p> + We need to generate a number of samples oriented along the normal of a surface. As we briefly discussed at the start of this chapter, we want to generate samples that form a hemisphere. As it is difficult nor plausible to generate a sample kernel for each surface normal direction, we're going to generate a sample kernel in <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" target="_blank">tangent space</a>, with the normal vector pointing in the positive z direction. +</p> + + <img src="/img/advanced-lighting/ssao_hemisphere.png" class="clean" alt="Image of normal oriented hemisphere sample kernel for use in SSAO in OpenGL"/> + +<p> + Assuming we have a unit hemisphere, we can obtain a sample kernel with a maximum of <code>64</code> sample values as follows: +</p> + +<pre><code> +std::uniform_real_distribution&lt;float&gt; randomFloats(0.0, 1.0); // random floats between [0.0, 1.0] +std::default_random_engine generator; +std::vector&lt;glm::vec3&gt; ssaoKernel; +for (unsigned int i = 0; i &lt; 64; ++i) +{ + glm::vec3 sample( + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) + ); + sample = glm::normalize(sample); + sample *= randomFloats(generator); + ssaoKernel.push_back(sample); +} +</code></pre> + +<p> + We vary the <code>x</code> and <code>y</code> direction in tangent space between <code>-1.0</code> and <code>1.0</code>, and vary the z direction of the samples between <code>0.0</code> and <code>1.0</code> (if we varied the z direction between <code>-1.0</code> and <code>1.0</code> as well we'd have a sphere sample kernel). As the sample kernel will be oriented along the surface normal, the resulting sample vectors will all end up in the hemisphere. +</p> + +<p> + Currently, all samples are randomly distributed in the sample kernel, but we'd rather place a larger weight on occlusions close to the actual fragment. We want to distribute more kernel samples closer to the origin. We can do this with an accelerating interpolation function: +</p> + +<pre><code> + float scale = (float)i / 64.0; + scale = lerp(0.1f, 1.0f, scale * scale); + sample *= scale; + ssaoKernel.push_back(sample); +} +</code></pre> + +<p> + Where <fun>lerp</fun> is defined as: +</p> + +<pre><code> +float lerp(float a, float b, float f) +{ + return a + f * (b - a); +} +</code></pre> + +<p> + This gives us a kernel distribution that places most samples closer to its origin. +</p> + +<img src="/img/advanced-lighting/ssao_kernel_weight.png" class="clean" alt="SSAO Sample kernels (normal oriented hemisphere) with samples more closer aligned to the fragment's center position in OpenGL"/> + + +<p> + Each of the kernel samples will be used to offset the view-space fragment position to sample surrounding geometry. We do need quite a lot of samples in view-space in order to get realistic results, which may be too heavy on performance. However, if we can introduce some semi-random rotation/noise on a per-fragment basis, we can significantly reduce the number of samples required. +</p> + +<h2>Random kernel rotations</h2> +<p> + By introducing some randomness onto the sample kernels we largely reduce the number of samples necessary to get good results. We could create a random rotation vector for each fragment of a scene, but that quickly eats up memory. It makes more sense to create a small texture of random rotation vectors that we tile over the screen. +</p> + +<p> + We create a 4x4 array of random rotation vectors oriented around the tangent-space surface normal: +</p> + +<pre><code> +std::vector&lt;glm::vec3&gt; ssaoNoise; +for (unsigned int i = 0; i &lt; 16; i++) +{ + glm::vec3 noise( + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) * 2.0 - 1.0, + 0.0f); + ssaoNoise.push_back(noise); +} +</code></pre> + +<p> + As the sample kernel is oriented along the positive z direction in tangent space, we leave the <code>z</code> component at <code>0.0</code> so we rotate around the <code>z</code> axis. +</p> + +<p> + We then create a 4x4 texture that holds the random rotation vectors; make sure to set its wrapping method to <var>GL_REPEAT</var> so it properly tiles over the screen. +</p> + +<pre><code> +unsigned int noiseTexture; +<function id='50'>glGenTextures</function>(1, &noiseTexture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, noiseTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); +</code></pre> + +<p> + We now have all the relevant input data we need to implement SSAO. +</p> + +<h2>The SSAO shader</h2> +<p> + The SSAO shader runs on a 2D screen-filled quad that calculates the occlusion value for each of its fragments. As we need to store the result of the SSAO stage (for use in the final lighting shader), we create yet another framebuffer object: + </p> + +<pre><code> +unsigned int ssaoFBO; +<function id='76'>glGenFramebuffers</function>(1, &ssaoFBO); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, ssaoFBO); + +unsigned int ssaoColorBuffer; +<function id='50'>glGenTextures</function>(1, &ssaoColorBuffer); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, ssaoColorBuffer); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBuffer, 0); +</code></pre> + +<p> + As the ambient occlusion result is a single grayscale value we'll only need a texture's red component, so we set the color buffer's internal format to <var>GL_RED</var>. +</p> + +<p> + The complete process for rendering SSAO then looks a bit like this: +</p> + +<pre><code> +// geometry pass: render stuff into G-buffer +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, gBuffer); + [...] +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); + +// use G-buffer to render SSAO texture +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, ssaoFBO); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT); + <function id='49'>glActiveTexture</function>(GL_TEXTURE0); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gPosition); + <function id='49'>glActiveTexture</function>(GL_TEXTURE1); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, gNormal); + <function id='49'>glActiveTexture</function>(GL_TEXTURE2); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, noiseTexture); + shaderSSAO.use(); + SendKernelSamplesToShader(); + shaderSSAO.setMat4("projection", projection); + RenderQuad(); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); + +// lighting pass: render scene lighting +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +shaderLightingPass.use(); +[...] +<function id='49'>glActiveTexture</function>(GL_TEXTURE3); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, ssaoColorBuffer); +[...] +RenderQuad(); +</code></pre> + +<p> + The <var>shaderSSAO</var> shader takes as input the relevant G-buffer textures, the noise texture, and the normal-oriented hemisphere kernel samples: +</p> + +<pre><code> +#version 330 core +out float FragColor; + +in vec2 TexCoords; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D texNoise; + +uniform vec3 samples[64]; +uniform mat4 projection; + +// tile noise texture over screen, based on screen dimensions divided by noise size +const vec2 noiseScale = vec2(800.0/4.0, 600.0/4.0); // screen = 800x600 + +void main() +{ + [...] +} +</code></pre> + +<p> + Interesting to note here is the <var>noiseScale</var> variable. We want to tile the noise texture all over the screen, but as the <var>TexCoords</var> vary between <code>0.0</code> and <code>1.0</code>, the <var>texNoise</var> texture won't tile at all. So we'll calculate the required amount to scale <var>TexCoords</var> by dividing the screen's dimensions by the noise texture size. +</p> + +<pre><code> +vec3 fragPos = texture(gPosition, TexCoords).xyz; +vec3 normal = texture(gNormal, TexCoords).rgb; +vec3 randomVec = texture(texNoise, TexCoords * noiseScale).xyz; +</code></pre> + +<p> + As we set the tiling parameters of <var>texNoise</var> to <var>GL_REPEAT</var>, the random values will be repeated all over the screen. Together with the <var>fragPos</var> and <var>normal</var> vector, we then have enough data to create a TBN matrix that transforms any vector from tangent-space to view-space: +</p> + +<pre><code> +vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); +vec3 bitangent = cross(normal, tangent); +mat3 TBN = mat3(tangent, bitangent, normal); +</code></pre> + +<p> + Using a process called the <def>Gramm-Schmidt process</def> we create an orthogonal basis, each time slightly tilted based on the value of <var>randomVec</var>. Note that because we use a random vector for constructing the tangent vector, there is no need to have the TBN matrix exactly aligned to the geometry's surface, thus no need for per-vertex tangent (and bitangent) vectors. +</p> + +<p> + Next we iterate over each of the kernel samples, transform the samples from tangent to view-space, add them to the current fragment position, and compare the fragment position's depth with the sample depth stored in the view-space position buffer. Let's discuss this in a step-by-step fashion: +</p> + +<pre><code> +float occlusion = 0.0; +for(int i = 0; i &lt; kernelSize; ++i) +{ + // get sample position + vec3 samplePos = TBN * samples[i]; // from tangent to view-space + samplePos = fragPos + samplePos * radius; + + [...] +} +</code></pre> + +<p> + Here <var>kernelSize</var> and <var>radius</var> are variables that we can use to tweak the effect; in this case a value of <var>64</var> and <var>0.5</var> respectively. + For each iteration we first transform the respective sample to view-space. We then add the view-space kernel offset sample to the view-space fragment position. Then we multiply the offset sample by <var>radius</var> to increase (or decrease) the effective sample radius of SSAO. +</p> + +<p> + Next we want to transform <var>sample</var> to screen-space so we can sample the position/depth value of <var>sample</var> as if we were rendering its position directly to the screen. As the vector is currently in view-space, we'll transform it to clip-space first using the <var>projection</var> matrix uniform: +</p> + +<pre><code> +vec4 offset = vec4(samplePos, 1.0); +offset = projection * offset; // from view to clip-space +offset.xyz /= offset.w; // perspective divide +offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0 +</code></pre> + +<p> + After the variable is transformed to clip-space, we perform the perspective divide step by dividing its <code>xyz</code> components with its <code>w</code> component. The resulting normalized device coordinates are then transformed to the [<code>0.0</code>, <code>1.0</code>] range so we can use them to sample the position texture: +</p> + +<pre><code> +float sampleDepth = texture(gPosition, offset.xy).z; +</code></pre> + +<p> + We use the <var>offset</var> vector's <code>x</code> and <code>y</code> component to sample the position texture to retrieve the depth (or <code>z</code> value) of the sample position as seen from the viewer's perspective (the first non-occluded visible fragment). We then check if the sample's current depth value is larger than the stored depth value and if so, we add to the final contribution factor: +</p> + +<pre class="cpp"><code> +occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0); +</code></pre> + +<p> + Note that we add a small <code>bias</code> here to the original fragment's depth value (set to <code>0.025</code> in this example). A bias isn't always necessary, but it helps visually tweak the SSAO effect and solves acne effects that may occur based on the scene's complexity. +</p> + +<p> + We're not completely finished yet as there is still a small issue we have to take into account. Whenever a fragment is tested for ambient occlusion that is aligned close to the edge of a surface, it will also consider depth values of surfaces far behind the test surface; these values will (incorrectly) contribute to the occlusion factor. We can solve this by introducing a range check as the following image (courtesy of <a href="http://john-chapman-graphics.blogspot.com/" target="_blank">John Chapman</a>) illustrates: +</p> + + <img src="/img/advanced-lighting/ssao_range_check.png" alt="Image with and without range check of SSAO surface in OpenGL"/> + +<p> + We introduce a range check that makes sure a fragment contributes to the occlusion factor if its depth values is within the sample's radius. We change the last line to: +</p> + +<pre><code> +float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth)); +occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; +</code></pre> + +<p> + Here we used GLSL's <fun>smoothstep</fun> function that smoothly interpolates its third parameter between the first and second parameter's range, returning <code>0.0</code> if less than or equal to its first parameter and <code>1.0</code> if equal or higher to its second parameter. If the depth difference ends up between <var>radius</var>, its value gets smoothly interpolated between <code>0.0</code> and <code>1.0</code> by the following curve: +</p> + + <img src="/img/advanced-lighting/ssao_smoothstep.png" class="clean" alt="Image of smoothstep function in OpenGL used for rangecheck in SSAO in OpenGL"/> + +<p> + If we were to use a hard cut-off range check that would abruptly remove occlusion contributions if the depth values are outside <var>radius</var>, we'd see obvious (unattractive) borders at where the range check is applied. +</p> + +<p> + As a final step we normalize the occlusion contribution by the size of the kernel and output the results. Note that we subtract the occlusion factor from <code>1.0</code> so we can directly use the occlusion factor to scale the ambient lighting component. +</p> + +<pre class="cpp"><code> +} +occlusion = 1.0 - (occlusion / kernelSize); +FragColor = occlusion; +</code></pre> + +<p> + If we'd imagine a scene where our favorite backpack model is taking a little nap, the ambient occlusion shader produces the following texture: +</p> + +<img src="/img/advanced-lighting/ssao_without_blur.png" class="clean" alt="Image of SSAO shader result in OpenGL"/> + +<p> + As we can see, ambient occlusion gives a great sense of depth. With just the ambient occlusion texture we can already clearly see the model is indeed laying on the floor, instead of hovering slightly above it. +</p> + +<p> + It still doesn't look perfect, as the repeating pattern of the noise texture is clearly visible. To create a smooth ambient occlusion result we need to blur the ambient occlusion texture. +</p> + +<h2>Ambient occlusion blur</h2> +<p> + Between the SSAO pass and the lighting pass, we first want to blur the SSAO texture. So let's create yet another framebuffer object for storing the blur result: +</p> + +<pre><code> +unsigned int ssaoBlurFBO, ssaoColorBufferBlur; +<function id='76'>glGenFramebuffers</function>(1, &ssaoBlurFBO); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, ssaoBlurFBO); +<function id='50'>glGenTextures</function>(1, &ssaoColorBufferBlur); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, ssaoColorBufferBlur); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RED, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoColorBufferBlur, 0); +</code></pre> + +<p> + Because the tiled random vector texture gives us a consistent randomness, we can use this property to our advantage to create a simple blur shader: +</p> + +<pre><code> +#version 330 core +out float FragColor; + +in vec2 TexCoords; + +uniform sampler2D ssaoInput; + +void main() { + vec2 texelSize = 1.0 / vec2(textureSize(ssaoInput, 0)); + float result = 0.0; + for (int x = -2; x &lt; 2; ++x) + { + for (int y = -2; y &lt; 2; ++y) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + result += texture(ssaoInput, TexCoords + offset).r; + } + } + FragColor = result / (4.0 * 4.0); +} +</code></pre> + +<p> + Here we traverse the surrounding SSAO texels between <code>-2.0</code> and <code>2.0</code>, sampling the SSAO texture an amount identical to the noise texture's dimensions. We offset each texture coordinate by the exact size of a single texel using <fun>textureSize</fun> that returns a <code>vec2</code> of the given texture's dimensions. We average the obtained results to get a simple, but effective blur: +</p> + + <img src="/img/advanced-lighting/ssao.png" class="clean" alt="Image of SSAO texture with blur applied in OpenGL"/> + +<p> + And there we go, a texture with per-fragment ambient occlusion data; ready for use in the lighting pass. +</p> + +<h2>Applying ambient occlusion</h2> +<p> + Applying the occlusion factors to the lighting equation is incredibly easy: all we have to do is multiply the per-fragment ambient occlusion factor to the lighting's ambient component and we're done. If we take the Blinn-Phong deferred lighting shader of the previous chapter and adjust it a bit, we get the following fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedo; +uniform sampler2D ssao; + +struct Light { + vec3 Position; + vec3 Color; + + float Linear; + float Quadratic; + float Radius; +}; +uniform Light light; + +void main() +{ + // retrieve data from gbuffer + vec3 FragPos = texture(gPosition, TexCoords).rgb; + vec3 Normal = texture(gNormal, TexCoords).rgb; + vec3 Diffuse = texture(gAlbedo, TexCoords).rgb; + float AmbientOcclusion = texture(ssao, TexCoords).r; + + // blinn-phong (in view-space) + vec3 ambient = vec3(0.3 * Diffuse * AmbientOcclusion); // here we add occlusion factor + vec3 lighting = ambient; + vec3 viewDir = normalize(-FragPos); // viewpos is (0.0.0) in view-space + // diffuse + vec3 lightDir = normalize(light.Position - FragPos); + vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color; + // specular + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(Normal, halfwayDir), 0.0), 8.0); + vec3 specular = light.Color * spec; + // attenuation + float dist = length(light.Position - FragPos); + float attenuation = 1.0 / (1.0 + light.Linear * dist + light.Quadratic * dist * dist); + diffuse *= attenuation; + specular *= attenuation; + lighting += diffuse + specular; + + FragColor = vec4(lighting, 1.0); +} +</code></pre> + +<p> + The only thing (aside from the change to view-space) we really changed is the multiplication of the scene's ambient component by <var>AmbientOcclusion</var>. With a single blue-ish point light in the scene we'd get the following result: +</p> + +<img src="/img/advanced-lighting/ssao_final.png" class="clean" alt="Image of SSAO applied in OpenGL"/> + +<p> + You can find the full source code of the demo scene <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/9.ssao/ssao.cpp" target="_blank">here</a>. +</p> + +<!--<ul> + <li><strong>geometry</strong>: <a href="/code_viewer.php?code=advanced-lighting/ssao_geometry&type=vertex" target="_blank">vertex</a>, <a href="/code_viewer.php?code=advanced-lighting/ssao_geometry&type=fragment" target="_blank">fragment</a>.</li> + <li><strong>SSAO</strong>: <a href="/code_viewer.php?code=advanced-lighting/ssao&type=vertex" target="_blank">vertex</a>, <a href="/code_viewer.php?code=advanced-lighting/ssao&type=fragment" target="_blank">fragment</a>.</li> + <li><strong>blur</strong>: <a href="/code_viewer.php?code=advanced-lighting/ssao&type=vertex" target="_blank">vertex</a>, <a href="/code_viewer.php?code=advanced-lighting/ssao_blur&type=fragment" target="_blank">fragment</a>.</li> + <li><strong>lighting</strong>: <a href="/code_viewer.php?code=advanced-lighting/ssao&type=vertex" target="_blank">vertex</a>, <a href="/code_viewer.php?code=advanced-lighting/ssao_lighting&type=fragment" target="_blank">fragment</a>.</li> +</ul> +--> + +<p> + Screen-space ambient occlusion is a highly customizable effect that relies heavily on tweaking its parameters based on the type of scene. There is no perfect combination of parameters for every type of scene. Some scenes only work with a small radius, while other scenes require a larger radius and a larger sample count for them to look realistic. The current demo uses <code>64</code> samples, which is a bit much; play around with a smaller kernel size and try to get good results. +</p> + +<p> + Some parameters you can tweak (by using uniforms for example): kernel size, radius, bias, and/or the size of the noise kernel. You can also raise the final occlusion value to a user-defined power to increase its strength: +</p> + +<pre><code> +occlusion = 1.0 - (occlusion / kernelSize); +FragColor = pow(occlusion, power); +</code></pre> + +<p> + Play around with different scenes and different parameters to appreciate the customizability of SSAO.</p> + +<p> + Even though SSAO is a subtle effect that isn't too clearly noticeable, it adds a great deal of realism to properly lit scenes and is definitely a technique you'd want to have in your toolkit. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html" target="_blank">SSAO Tutorial</a>: excellent SSAO tutorial by John Chapman; a large portion of this chapter's code and techniques are based of his article.</li> + <li><a href="https://mtnphil.wordpress.com/2013/06/26/know-your-ssao-artifacts/" target="_blank">Know your SSAO artifacts</a>: great article about improving SSAO specific artifacts.</li> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial46/tutorial46.html" target="_blank">SSAO With Depth Reconstruction</a>: extension tutorial on top of SSAO from OGLDev about reconstructing position vectors from depth alone, saving us from storing the expensive position vectors in the G-buffer.</li> + </ul> + + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Shadows/Point-Shadows.html b/pub/Advanced-Lighting/Shadows/Point-Shadows.html @@ -0,0 +1,926 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Point Shadows</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Shadows/Point-Shadows</h1> +<p> + In the last chapter we learned to create dynamic shadows with shadow mapping. It works great, but it's mostly suited for directional (or spot) lights as the shadows are generated only in the direction of the light source. It is therefore also known as <def>directional shadow mapping</def> as the depth (or shadow) map is generated from only the direction the light is looking at. +</p> + +<p> + What this chapter will focus on is the generation of dynamic shadows in all surrounding directions. The technique we're using is perfect for point lights as a real point light would cast shadows in all directions. This technique is known as point (light) shadows or more formerly as <def>omnidirectional shadow maps</def>. +</p> + +<note> + This chapter builds upon the previous <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping" target="_blank">shadow mapping</a> chapter so unless you're familiar with traditional shadow mapping it is advised to read the shadow mapping chapter first. +</note> + +<p> + The technique is mostly similar to directional shadow mapping: we generate a depth map from the light's perspective(s), sample the depth map based on the current fragment position, and compare each fragment with the stored depth value to see whether it is in shadow. The main difference between directional shadow mapping and omnidirectional shadow mapping is the depth map we use. +</p> + +<p> + The depth map we need requires rendering a scene from all surrounding directions of a point light and as such a normal 2D depth map won't work; what if we were to use a <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps" target="_blank">cubemap</a> instead? Because a cubemap can store full environment data with only 6 faces, it is possible to render the entire scene to each of the faces of a cubemap and sample these as the point light's surrounding depth values. +</p> + +<img src="/img/advanced-lighting/point_shadows_diagram.png" class="clean" alt="Image of how omnidrectional shadow mapping or point shadows work"/> + +<p> + The generated depth cubemap is then passed to the lighting fragment shader that samples the cubemap with a direction vector to obtain the closest depth (from the light's perspective) at that fragment. Most of the complicated stuff we've already discussed in the shadow mapping chapter. What makes this technique a bit more difficult is the depth cubemap generation. +</p> + +<h2>Generating the depth cubemap</h2> +<p> + To create a cubemap of a light's surrounding depth values we have to render the scene 6 times: once for each face. One (quite obvious) way to do this, is render the scene 6 times with 6 different view matrices, each time attaching a different cubemap face to the framebuffer object. This would look something like this: + </p> + +<pre><code> +for(unsigned int i = 0; i &lt; 6; i++) +{ + GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + <function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, face, depthCubemap, 0); + BindViewMatrix(lightViewMatrices[i]); + RenderScene(); +} +</code></pre> + +<p> + This can be quite expensive though as a lot of render calls are necessary for this single depth map. In this chapter we're going to use an alternative (more organized) approach using a little trick in the geometry shader that allows us to build the depth cubemap with just a single render pass. +</p> + +<p> + First, we'll need to create a cubemap: +</p> + +<pre><code> +unsigned int depthCubemap; +<function id='50'>glGenTextures</function>(1, &depthCubemap); +</code></pre> + +<p> + And assign each of the single cubemap faces a 2D depth-valued texture image: +</p> + +<pre><code> +const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, depthCubemap); +for (unsigned int i = 0; i &lt; 6; ++i) + <function id='52'>glTexImage2D</function>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, + SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); +</code></pre> + +<p> + And don't forget to set the texture parameters: +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +</code></pre> + +<p> + Normally we'd attach a single face of a cubemap texture to the framebuffer object and render the scene 6 times, each time switching the depth buffer target of the framebuffer to a different cubemap face. Since we're going to use a geometry shader, that allows us to render to all faces in a single pass, we can directly attach the cubemap as a framebuffer's depth attachment with <fun>glFramebufferTexture</fun>: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, depthMapFBO); +glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap, 0); +glDrawBuffer(GL_NONE); +glReadBuffer(GL_NONE); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + Again, note the call to <fun>glDrawBuffer</fun> and <fun>glReadBuffer</fun>: we only care about depth values when generating a depth cubemap so we have to explicitly tell OpenGL this framebuffer object does not render to a color buffer. +</p> + +<p> + With omnidirectional shadow maps we have two render passes: first, we generate the depth cubemap and second, we use the depth cubemap in the normal render pass to add shadows to the scene. This process looks a bit like this: +</p> + +<pre><code> +// 1. first render to depth cubemap +<function id='22'>glViewport</function>(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, depthMapFBO); + <function id='10'>glClear</function>(GL_DEPTH_BUFFER_BIT); + ConfigureShaderAndMatrices(); + RenderScene(); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +// 2. then render scene as normal with shadow mapping (using depth cubemap) +<function id='22'>glViewport</function>(0, 0, SCR_WIDTH, SCR_HEIGHT); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +ConfigureShaderAndMatrices(); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, depthCubemap); +RenderScene(); +</code></pre> + +<p> + The process is exactly the same as with default shadow mapping, although this time we render to and use a cubemap depth texture compared to a 2D depth texture. +</p> + +<h3>Light space transform</h3> +<p> + With the framebuffer and cubemap set, we need some way to transform all the scene's geometry to the relevant light spaces in all 6 directions of the light. Just like the <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping" target="_blank">shadow mapping</a> chapter we're going to need a light space transformation matrix \(T\), but this time one for each face. +</p> + +<p> + Each light space transformation matrix contains both a projection and a view matrix. For the projection matrix we're going to use a perspective projection matrix; the light source represents a point in space so perspective projection makes most sense. Each light space transformation matrix uses the same projection matrix: +</p> + +<pre><code> +float aspect = (float)SHADOW_WIDTH/(float)SHADOW_HEIGHT; +float near = 1.0f; +float far = 25.0f; +glm::mat4 shadowProj = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(90.0f), aspect, near, far); +</code></pre> + +<p> + Important to note here is the field of view parameter of <fun><function id='58'>glm::perspective</function></fun> that we set to 90 degrees. By setting this to 90 degrees we make sure the viewing field is exactly large enough to fill a single face of the cubemap such that all faces align correctly to each other at the edges. +</p> + +<p> + As the projection matrix does not change per direction we can re-use it for each of the 6 transformation matrices. We do need a different view matrix per direction. With <fun><function id='62'>glm::lookAt</function></fun> we create 6 view directions, each looking at one face direction of the cubemap in the order: right, left, top, bottom, near and far. +</p> + +<pre><code> +std::vector&lt;glm::mat4&gt; shadowTransforms; +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3( 1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)); +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)); +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3( 0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0)); +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3( 0.0,-1.0, 0.0), glm::vec3(0.0, 0.0,-1.0)); +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3( 0.0, 0.0, 1.0), glm::vec3(0.0,-1.0, 0.0)); +shadowTransforms.push_back(shadowProj * + <function id='62'>glm::lookAt</function>(lightPos, lightPos + glm::vec3( 0.0, 0.0,-1.0), glm::vec3(0.0,-1.0, 0.0)); +</code></pre> + +<p> + Here we create 6 view matrices and multiply them with the projection matrix to get a total of 6 different light space transformation matrices. The <code>target</code> parameter of <fun><function id='62'>glm::lookAt</function></fun> each looks into the direction of a single cubemap face. +</p> + +<p> + These transformation matrices are sent to the shaders that render the depth into the cubemap. +</p> + +<h3>Depth shaders</h3> +<p> + To render depth values to a depth cubemap we're going to need a total of three shaders: a vertex and fragment shader, and a <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader" target="_blank">geometry shader</a> in between. +</p> + +<p> + The geometry shader will be the shader responsible for transforming all world-space vertices to the 6 different light spaces. Therefore, the vertex shader simply transforms vertices to world-space and directs them to the geometry shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 model; + +void main() +{ + gl_Position = model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + The geometry shader will take as input 3 triangle vertices and a uniform array of light space transformation matrices. The geometry shader is responsible for transforming the vertices to the light spaces; this is also where it gets interesting. + </p> + + <p> + The geometry shader has a built-in variable called <var>gl_Layer</var> that specifies which cubemap face to emit a primitive to. When left alone, the geometry shader just sends its primitives further down the pipeline as usual, but when we update this variable we can control to which cubemap face we render to for each primitive. This of course only works when we have a cubemap texture attached to the active framebuffer. +</p> + +<pre><code> +#version 330 core +layout (triangles) in; +layout (triangle_strip, max_vertices=18) out; + +uniform mat4 shadowMatrices[6]; + +out vec4 FragPos; // FragPos from GS (output per emitvertex) + +void main() +{ + for(int face = 0; face &lt; 6; ++face) + { + gl_Layer = face; // built-in variable that specifies to which face we render. + for(int i = 0; i &lt; 3; ++i) // for each triangle vertex + { + FragPos = gl_in[i].gl_Position; + gl_Position = shadowMatrices[face] * FragPos; + EmitVertex(); + } + EndPrimitive(); + } +} +</code></pre> + +<p> + This geometry shader is relatively straightforward. We take as input a triangle, and output a total of 6 triangles (6 * 3 equals 18 vertices). In the <fun>main</fun> function we iterate over 6 cubemap faces where we specify each face as the output face by storing the face integer into <var>gl_Layer</var>. We then generate the output triangles by transforming each world-space input vertex to the relevant light space by multiplying <var>FragPos</var> with the face's light-space transformation matrix. Note that we also sent the resulting <var>FragPos</var> variable to the fragment shader that we'll need to calculate a depth value. +</p> + +<p> + In the last chapter we used an empty fragment shader and let OpenGL figure out the depth values of the depth map. This time we're going to calculate our own (linear) depth as the linear distance between each closest fragment position and the light source's position. Calculating our own depth values makes the later shadow calculations a bit more intuitive. +</p> + +<pre><code> +#version 330 core +in vec4 FragPos; + +uniform vec3 lightPos; +uniform float far_plane; + +void main() +{ + // get distance between fragment and light source + float lightDistance = length(FragPos.xyz - lightPos); + + // map to [0;1] range by dividing by far_plane + lightDistance = lightDistance / far_plane; + + // write this as modified depth + gl_FragDepth = lightDistance; +} +</code></pre> + +<p> + The fragment shader takes as input the <var>FragPos</var> from the geometry shader, the light's position vector, and the frustum's far plane value. Here we take the distance between the fragment and the light source, map it to the [<code>0</code>,<code>1</code>] range and write it as the fragment's depth value. +</p> + +<p> + Rendering the scene with these shaders and the cubemap-attached framebuffer object active should give you a completely filled depth cubemap for the second pass's shadow calculations. +</p> + +<h2>Omnidirectional shadow maps</h2> +<p> + With everything set up it is time to render the actual omnidirectional shadows. The procedure is similar to the directional shadow mapping chapter, although this time we bind a cubemap texture instead of a 2D texture and also pass the light projection's far plane variable to the shaders. +</p> + +<pre><code> +<function id='22'>glViewport</function>(0, 0, SCR_WIDTH, SCR_HEIGHT); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +shader.use(); +// ... send uniforms to shader (including light's far_plane value) +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, depthCubemap); +// ... bind other textures +RenderScene(); +</code></pre> + +<p> + Here the <fun>renderScene</fun> function renders a few cubes in a large cube room scattered around a light source at the center of the scene. +</p> + +<p> + The vertex and fragment shader are mostly similar to the original shadow mapping shaders: the difference being that the fragment shader no longer requires a fragment position in light space as we can now sample the depth values with a direction vector. +</p> + +<p> + Because of this, the vertex shader doesn't needs to transform its position vectors to light space so we can remove the <var>FragPosLightSpace</var> variable: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; + +out vec2 TexCoords; + +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; +} vs_out; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main() +{ + vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); + vs_out.Normal = transpose(inverse(mat3(model))) * aNormal; + vs_out.TexCoords = aTexCoords; + gl_Position = projection * view * model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + The fragment shader's Blinn-Phong lighting code is exactly the same as we had before with a shadow multiplication at the end: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; +} fs_in; + +uniform sampler2D diffuseTexture; +uniform samplerCube depthMap; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +uniform float far_plane; + +float ShadowCalculation(vec3 fragPos) +{ + [...] +} + +void main() +{ + vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; + vec3 normal = normalize(fs_in.Normal); + vec3 lightColor = vec3(0.3); + // ambient + vec3 ambient = 0.3 * color; + // diffuse + vec3 lightDir = normalize(lightPos - fs_in.FragPos); + float diff = max(dot(lightDir, normal), 0.0); + vec3 diffuse = diff * lightColor; + // specular + vec3 viewDir = normalize(viewPos - fs_in.FragPos); + vec3 reflectDir = reflect(-lightDir, normal); + float spec = 0.0; + vec3 halfwayDir = normalize(lightDir + viewDir); + spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); + vec3 specular = spec * lightColor; + // calculate shadow + float shadow = ShadowCalculation(fs_in.FragPos); + vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; + + FragColor = vec4(lighting, 1.0); +} +</code></pre> + +<p> + There are a few subtle differences: the lighting code is the same, but we now have a <code>samplerCube</code> uniform and the <fun>ShadowCalculation</fun> function takes the current fragment's position as its argument instead of the fragment position in light space. We now also include the light frustum's <var>far_plane</var> value that we'll later need. +</p> + +<p> + The biggest difference is in the content of the <fun>ShadowCalculation</fun> function that now samples depth values from a cubemap instead of a 2D texture. Let's discuss its content step by step. +</p> + +<p> + The first thing we have to do is retrieve the depth of the cubemap. You may remember from the cubemap section of this chapter that we stored the depth as the linear distance between the fragment and the light position; we're taking a similar approach here: +</p> + +<pre><code> +float ShadowCalculation(vec3 fragPos) +{ + vec3 fragToLight = fragPos - lightPos; + float closestDepth = texture(depthMap, fragToLight).r; +} +</code></pre> + +<p> + Here we take the difference vector between the fragment's position and the light's position and use that vector as a direction vector to sample the cubemap. The direction vector doesn't need to be a unit vector to sample from a cubemap so there's no need to normalize it. The resulting <var>closestDepth</var> value is the normalized depth value between the light source and its closest visible fragment. +</p> + +<p> + The <var>closestDepth</var> value is currently in the range [<code>0</code>,<code>1</code>] so we first transform it back to [<code>0</code>,<code>far_plane</code>] by multiplying it with <var>far_plane</var>. +</p> + +<pre><code> +closestDepth *= far_plane; +</code></pre> + +<p> + Next we retrieve the depth value between the current fragment and the light source, which we can easily obtain by taking the length of <var>fragToLight</var> due to how we calculated depth values in the cubemap: +</p> + +<pre><code> +float currentDepth = length(fragToLight); +</code></pre> + +<p> + This returns a depth value in the same (or larger) range as <var>closestDepth</var>. +</p> + +<p> + Now we can compare both depth values to see which is closer than the other and determine whether the current fragment is in shadow. We also include a shadow bias so we don't get shadow acne as discussed in the <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping" target="_blank">previous</a> chapter. +</p> + +<pre><code> +float bias = 0.05; +float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; +</code></pre> + +<p> + The complete <fun>ShadowCalculation</fun> then becomes: +</p> + +<pre><code> +float ShadowCalculation(vec3 fragPos) +{ + // get vector between fragment position and light position + vec3 fragToLight = fragPos - lightPos; + // use the light to fragment vector to sample from the depth map + float closestDepth = texture(depthMap, fragToLight).r; + // it is currently in linear range between [0,1]. Re-transform back to original value + closestDepth *= far_plane; + // now get current linear depth as the length between the fragment and light position + float currentDepth = length(fragToLight); + // now test for shadows + float bias = 0.05; + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + + return shadow; +} +</code></pre> + +<p> + With these shaders we already get pretty good shadows and this time in all surrounding directions from a point light. With a point light positioned at the center of a simple scene it'll look a bit like this: +</p> + + <img src="/img/advanced-lighting/point_shadows.png" class="clean" alt="Omnidirectional point shadow maps in OpenGL"/> + +<p> + You can find the source code of this demo <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/3.2.1.point_shadows/point_shadows.cpp" target="_blank">here</a>. +</p> + +<h3>Visualizing cubemap depth buffer</h3> +<p> + If you're somewhat like me you probably didn't get this right on the first try so it makes sense to do some debugging, with one of the obvious checks being validating whether the depth map was built correctly. A simple trick to visualize the depth buffer is to take the <var>closestDepth</var> variable in the <fun>ShadowCalculation</fun> function and display that variable as: +</p> + +<pre><code> +FragColor = vec4(vec3(closestDepth / far_plane), 1.0); +</code></pre> + +<p> + The result is a grayed out scene where each color represents the linear depth values of the scene: +</p> + + <img src="/img/advanced-lighting/point_shadows_depth_cubemap.png" class="clean" alt="Visualized depth cube map of omnidrectional shadow maps"/> + +<p> + You can also see the to-be shadowed regions on the outside wall. If it looks somewhat similar, you know the depth cubemap was properly generated. +</p> + +<h2>PCF</h2> +<p> + Since omnidirectional shadow maps are based on the same principles of traditional shadow mapping it also has the same resolution dependent artifacts. If you zoom in close enough you can again see jagged edges. <def>Percentage-closer filtering</def> or PCF allows us to smooth out these jagged edges by filtering multiple samples around the fragment position and average the results. +</p> + +<p> + If we take the same simple PCF filter of the previous chapter and add a third dimension we get: +</p> + +<pre><code> +float shadow = 0.0; +float bias = 0.05; +float samples = 4.0; +float offset = 0.1; +for(float x = -offset; x &lt; offset; x += offset / (samples * 0.5)) +{ + for(float y = -offset; y &lt; offset; y += offset / (samples * 0.5)) + { + for(float z = -offset; z &lt; offset; z += offset / (samples * 0.5)) + { + float closestDepth = texture(depthMap, fragToLight + vec3(x, y, z)).r; + closestDepth *= far_plane; // undo mapping [0;1] + if(currentDepth - bias &gt; closestDepth) + shadow += 1.0; + } + } +} +shadow /= (samples * samples * samples); +</code></pre> + +<p> + The code isn't that different from the traditional shadow mapping code. We calculate and add texture offsets dynamically for each axis based on a fixed number of samples. For each sample we repeat the original shadow process on the offsetted sample direction and average the results at the end. +</p> + +<p> + The shadows now look more soft and smooth and give more plausible results. +</p> + + <img src="/img/advanced-lighting/point_shadows_soft.png" class="clean" alt="Soft shades with omnidirectional shadow mapping in OpenGL using PCF"/> + +<p> + However, with <var>samples</var> set to <code>4.0</code> we take a total of <code>64</code> samples each fragment which is a lot! +</p> + +<p> + As most of these samples are redundant in that they sample close to the original direction vector it may make more sense to only sample in perpendicular directions of the sample direction vector. However as there is no (easy) way to figure out which sub-directions are redundant this becomes difficult. One trick we can use is to take an array of offset directions that are all roughly separable e.g. each of them points in completely different directions. This will significantly reduce the number of sub-directions that are close together. Below we have such an array of a maximum of <code>20</code> offset directions: +</p> + +<pre><code> +vec3 sampleOffsetDirections[20] = vec3[] +( + vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), + vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) +); +</code></pre> + +<p> + From this we can adapt the PCF algorithm to take a fixed amount of samples from <var>sampleOffsetDirections</var> and use these to sample the cubemap. The advantage here is that we need a lot less samples to get visually similar results. +</p> + +<pre><code> +float shadow = 0.0; +float bias = 0.15; +int samples = 20; +float viewDistance = length(viewPos - fragPos); +float diskRadius = 0.05; +for(int i = 0; i &lt; samples; ++i) +{ + float closestDepth = texture(depthMap, fragToLight + sampleOffsetDirections[i] * diskRadius).r; + closestDepth *= far_plane; // undo mapping [0;1] + if(currentDepth - bias > closestDepth) + shadow += 1.0; +} +shadow /= float(samples); +</code></pre> + +<p> + Here we add multiple offsets, scaled by some <var>diskRadius</var>, around the original <var>fragToLight</var> direction vector to sample from the cubemap. +</p> + +<p> + Another interesting trick we can apply here is that we can change <var>diskRadius</var> based on the distance of the viewer to the fragment, making the shadows softer when far away and sharper when close by. +</p> + +<pre><code> +float diskRadius = (1.0 + (viewDistance / far_plane)) / 25.0; +</code></pre> + +<p> + The results of the updated PCF algorithm gives just as good, if not better, results of soft shadows: +</p> + +<img src="/img/advanced-lighting/point_shadows_soft_better.png" class="clean" alt="Soft shades with omnidirectional shadow mapping in OpenGL using PCF, more efficient"/> + +<p> + Of course, the <var>bias</var> we add to each sample is highly based on context and will always require tweaking based on the scene you're working with. Play around with all the values and see how they affect the scene. +</p> + +<p> + You can find the final code here: <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/3.2.2.point_shadows_soft/point_shadows_soft.cpp" target="_blank">here</a>. +</p> + +<p> + I should mention that using geometry shaders to generate a depth map isn't necessarily faster than rendering the scene 6 times for each face. Using a geometry shader like this has its own performance penalties that may outweigh the performance gain of using one in the first place. This of course depends on the type of environment, the specific video card drivers, and plenty of other factors. So if you really care about pushing the most out of your system, make sure to profile both methods and select the more efficient one for your scene. +</p> + +<h2>Additional resources</h2> + <ul> + <li><a href="http://www.sunandblackcat.com/tipFullView.php?l=eng&topicid=36" target="_blank">Shadow Mapping for point light sources in OpenGL</a>: omnidirectional shadow mapping tutorial by sunandblackcat.</li> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial43/tutorial43.html" target="_blank">Multipass Shadow Mapping With Point Lights</a>: omnidirectional shadow mapping tutorial by ogldev.</li> + <li><a href="http://www.cg.tuwien.ac.at/~husky/RTR/OmnidirShadows-whyCaps.pdf" target="_blank">Omni-directional Shadows</a>: a nice set of slides about omnidirectional shadow mapping by Peter Houska.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-Lighting/Shadows/Shadow-Mapping.html b/pub/Advanced-Lighting/Shadows/Shadow-Mapping.html @@ -0,0 +1,1047 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Shadow Mapping</h1> +<h1 id="content-url" style='display:none;'>Advanced-Lighting/Shadows/Shadow-Mapping</h1> +<p> + Shadows are a result of the absence of light due to occlusion. When a light source's light rays do not hit an object because it gets occluded by some other object, the object is in shadow. Shadows add a great deal of realism to a lit scene and make it easier for a viewer to observe spatial relationships between objects. They give a greater sense of depth to our scene and objects. For example, take a look at the following image of a scene with and without shadows: + +</p> + +<img src="/img/advanced-lighting/shadow_mapping_with_without.png" alt="comparrison of shadows in a scene with and without in OpenGL"/> + +<p> + You can see that with shadows it becomes much more obvious how the objects relate to each other. For instance, the fact that one of the cubes is floating above the others is only really noticeable when we have shadows. +</p> + +<p> + Shadows are a bit tricky to implement though, specifically because in current real-time (rasterized graphics) research a perfect shadow algorithm hasn't been developed yet. There are several good shadow approximation techniques, but they all have their little quirks and annoyances which we have to take into account. +</p> + +<p> + One technique used by most videogames that gives decent results and is relatively easy to implement is <def>shadow mapping</def>. Shadow mapping is not too difficult to understand, doesn't cost too much in performance and quite easily extends into more advanced algorithms (like <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows" target="_blank">Omnidirectional Shadow Maps</a> and Cascaded Shadow Maps). +</p> + +<h2>Shadow mapping</h2> +<p> + The idea behind shadow mapping is quite simple: we render the scene from the light's point of view and everything we see from the light's perspective is lit and everything we can't see must be in shadow. Imagine a floor section with a large box between itself and a light source. Since the light source will see this box and not the floor section when looking in its direction that specific floor section should be in shadow. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_theory.png" class="clean" alt="Shadow mapping illustrated."/> + +<p> + Here all the blue lines represent the fragments that the light source can see. The occluded fragments are shown as black lines: these are rendered as being shadowed. If we were to draw a line or <def>ray</def> from the light source to a fragment on the right-most box we can see the ray first hits the floating container before hitting the right-most container. As a result, the floating container's fragment is lit and the right-most container's fragment is not lit and thus in shadow. +</p> + +<p> + We want to get the point on the ray where it first hit an object and compare this <em>closest point</em> to other points on this ray. We then do a basic test to see if a test point's ray position is further down the ray than the closest point and if so, the test point must be in shadow. Iterating through possibly thousands of light rays from such a light source is an extremely inefficient approach and doesn't lend itself too well for real-time rendering. We can do something similar, but without casting light rays. Instead, we use something we're quite familiar with: the depth buffer. +</p> + +<p> + You may remember from the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter that a value in the depth buffer corresponds to the depth of a fragment clamped to [0,1] from the camera's point of view. What if we were to render the scene from the light's perspective and store the resulting depth values in a texture? This way, we can sample the closest depth values as seen from the light's perspective. After all, the depth values show the first fragment visible from the light's perspective. We store all these depth values in a texture that we call a <def>depth map</def> or <def>shadow map</def>. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_theory_spaces.png" class="clean" alt="Different coordinate transforms / spaces for shadow mapping."/> + +<p> + The left image shows a directional light source (all light rays are parallel) casting a shadow on the surface below the cube. Using the depth values stored in the depth map we find the closest point and use that to determine whether fragments are in shadow. We create the depth map by rendering the scene (from the light's perspective) using a view and projection matrix specific to that light source. This projection and view matrix together form a transformation \(T\) that transforms any 3D position to the light's (visible) coordinate space. +</p> + +<note> + A directional light doesn't have a position as it's modelled to be infinitely far away. However, for the sake of shadow mapping we need to render the scene from a light's perspective and thus render the scene from a position somewhere along the lines of the light direction. +</note> + + <p> + In the right image we see the same directional light and the viewer. We render a fragment at point \(\bar{\color{red}{P}}\) for which we have to determine whether it is in shadow. To do this, we first transform point \(\bar{\color{red}{P}}\) to the light's coordinate space using \(T\). Since point \(\bar{\color{red}{P}}\) is now as seen from the light's perspective, its <code>z</code> coordinate corresponds to its depth which in this example is <code>0.9</code>. Using point \(\bar{\color{red}{P}}\) we can also index the depth/shadow map to obtain the closest visible depth from the light's perspective, which is at point \(\bar{\color{green}{C}}\) with a sampled depth of <code>0.4</code>. Since indexing the depth map returns a depth smaller than the depth at point \(\bar{\color{red}{P}}\) we can conclude point \(\bar{\color{red}{P}}\) is occluded and thus in shadow. +</p> + + +<p> + Shadow mapping therefore consists of two passes: first we render the depth map, and in the second pass we render the scene as normal and use the generated depth map to calculate whether fragments are in shadow. It may sound a bit complicated, but as soon as we walk through the technique step-by-step it'll likely start to make sense. +</p> + +<h2>The depth map</h2> +<p> + The first pass requires us to generate a depth map. The depth map is the depth texture as rendered from the light's perspective that we'll be using for testing for shadows. Because we need to store the rendered result of a scene into a texture we're going to need <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> again. +</p> + +<p> + First we'll create a framebuffer object for rendering the depth map: +</p> + +<pre><code> +unsigned int depthMapFBO; +<function id='76'>glGenFramebuffers</function>(1, &depthMapFBO); +</code></pre> + +<p> + Next we create a 2D texture that we'll use as the framebuffer's depth buffer: +</p> + +<pre><code> +const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; + +unsigned int depthMap; +<function id='50'>glGenTextures</function>(1, &depthMap); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, depthMap); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, + SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); +</code></pre> + +<p> + Generating the depth map shouldn't look too complicated. Because we only care about depth values we specify the texture's formats as <var>GL_DEPTH_COMPONENT</var>. We also give the texture a width and height of <code>1024</code>: this is the resolution of the depth map. +</p> + +<p> + With the generated depth texture we can attach it as the framebuffer's depth buffer: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, depthMapFBO); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); +glDrawBuffer(GL_NONE); +glReadBuffer(GL_NONE); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + We only need the depth information when rendering the scene from the light's perspective so there is no need for a color buffer. A framebuffer object however is not complete without a color buffer so we need to explicitly tell OpenGL we're not going to render any color data. We do this by setting both the read and draw buffer to <var>GL_NONE</var> with <fun>glDrawBuffer</fun> and <fun>glReadbuffer</fun>. +</p> + +<p> + With a properly configured framebuffer that renders depth values to a texture we can start the first pass: generate the depth map. When combined with the second pass, the complete rendering stage will look a bit like this: +</p> + +<pre><code> +// 1. first render to depth map +<function id='22'>glViewport</function>(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, depthMapFBO); + <function id='10'>glClear</function>(GL_DEPTH_BUFFER_BIT); + ConfigureShaderAndMatrices(); + RenderScene(); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +// 2. then render scene as normal with shadow mapping (using depth map) +<function id='22'>glViewport</function>(0, 0, SCR_WIDTH, SCR_HEIGHT); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +ConfigureShaderAndMatrices(); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, depthMap); +RenderScene(); +</code></pre> + +<p> + This code left out some details, but it'll give you the general idea of shadow mapping. What is important to note here are the calls to <fun><function id='22'>glViewport</function></fun>. Because shadow maps often have a different resolution compared to what we originally render the scene in (usually the window resolution), we need to change the viewport parameters to accommodate for the size of the shadow map. If we forget to update the viewport parameters, the resulting depth map will be either incomplete or too small. +</p> + +<h3>Light space transform</h3> +<p> + An unknown in the previous snippet of code is the <fun>ConfigureShaderAndMatrices</fun> function. In the second pass this is business as usual: make sure proper projection and view matrices are set, and set the relevant model matrices per object. However, in the first pass we need to use a different projection and view matrix to render the scene from the light's point of view. +</p> + +<p> + Because we're modelling a directional light source, all its light rays are parallel. For this reason, we're going to use an orthographic projection matrix for the light source where there is no perspective deform: +</p> + +<pre><code> +float near_plane = 1.0f, far_plane = 7.5f; +glm::mat4 lightProjection = <function id='59'>glm::ortho</function>(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); +</code></pre> + +<p> + Here is an example orthographic projection matrix as used in this chapter's demo scene. Because a projection matrix indirectly determines the range of what is visible (e.g. what is not clipped) you want to make sure the size of the projection frustum correctly contains the objects you want to be in the depth map. When objects or fragments are not in the depth map they will not produce shadows. +</p> + +<p> + To create a view matrix to transform each object so they're visible from the light's point of view, we're going to use the infamous <fun><function id='62'>glm::lookAt</function></fun> function; this time with the light source's position looking at the scene's center. +</p> + +<pre><code> +glm::mat4 lightView = <function id='62'>glm::lookAt</function>(glm::vec3(-2.0f, 4.0f, -1.0f), + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 0.0f, 1.0f, 0.0f)); +</code></pre> + +<p> + Combining these two gives us a light space transformation matrix that transforms each world-space vector into the space as visible from the light source; exactly what we need to render the depth map. +</p> + +<pre><code> +glm::mat4 lightSpaceMatrix = lightProjection * lightView; +</code></pre> + +<p> + This <var>lightSpaceMatrix</var> is the transformation matrix that we earlier denoted as \(T\). With this <var>lightSpaceMatrix</var>, we can render the scene as usual as long as we give each shader the light-space equivalents of the projection and view matrices. However, we only care about depth values and not all the expensive fragment (lighting) calculations. To save performance we're going to use a different, but much simpler shader for rendering to the depth map. +</p> + +<h3>Render to depth map</h3> +<p> + When we render the scene from the light's perspective we'd much rather use a simple shader that only transforms the vertices to light space and not much more. For such a simple shader called <var>simpleDepthShader</var> we'll use the following vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 lightSpaceMatrix; +uniform mat4 model; + +void main() +{ + gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + This vertex shader takes a per-object model, a vertex, and transforms all vertices to light space using <var>lightSpaceMatrix</var>. +</p> + +<p> + Since we have no color buffer and disabled the draw and read buffers, the resulting fragments do not require any processing so we can simply use an empty fragment shader: +</p> + +<pre><code> +#version 330 core + +void main() +{ + // gl_FragDepth = gl_FragCoord.z; +} +</code></pre> + +<p> + This empty fragment shader does no processing whatsoever, and at the end of its run the depth buffer is updated. We could explicitly set the depth by uncommenting its one line, but this is effectively what happens behind the scene anyways. +</p> + +<p> + Rendering the depth/shadow map now effectively becomes: +</p> + +<pre><code> +simpleDepthShader.use(); +<function id='44'>glUniform</function>Matrix4fv(lightSpaceMatrixLocation, 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix)); + +<function id='22'>glViewport</function>(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, depthMapFBO); + <function id='10'>glClear</function>(GL_DEPTH_BUFFER_BIT); + RenderScene(simpleDepthShader); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + Here the <fun>RenderScene</fun> function takes a shader program, calls all relevant drawing functions and sets the corresponding model matrices where necessary. +</p> + +<p> + The result is a nicely filled depth buffer holding the closest depth of each visible fragment from the light's perspective. By rendering this texture onto a 2D quad that fills the screen (similar to what we did in the post-processing section at the end of the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> chapter) we get something like this: +</p> + +<img src="/img/advanced-lighting/shadow_mapping_depth_map.png" class="clean" alt="Depth (or shadow) map of shadow mapping technique"/> + +<p> + For rendering the depth map onto a quad we used the following fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D depthMap; + +void main() +{ + float depthValue = texture(depthMap, TexCoords).r; + FragColor = vec4(vec3(depthValue), 1.0); +} +</code></pre> + +<p> + Note that there are some subtle changes when displaying depth using a perspective projection matrix instead of an orthographic projection matrix as depth is non-linear when using perspective projection. At the end of this chapter we'll discuss some of these subtle differences. +</p> + +<p> + You can find the source code for rendering a scene to a depth map <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/3.1.1.shadow_mapping_depth/shadow_mapping_depth.cpp" target="_blank">here</a>. +</p> + +<h2>Rendering shadows</h2> +<p> + With a properly generated depth map we can start rendering the actual shadows. The code to check if a fragment is in shadow is (quite obviously) executed in the fragment shader, but we do the light-space transformation in the vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; + +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + vec4 FragPosLightSpace; +} vs_out; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; +uniform mat4 lightSpaceMatrix; + +void main() +{ + vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); + vs_out.Normal = transpose(inverse(mat3(model))) * aNormal; + vs_out.TexCoords = aTexCoords; + vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); + gl_Position = projection * view * vec4(vs_out.FragPos, 1.0); +} +</code></pre> + +<p> + What is new here is the extra output vector <var>FragPosLightSpace</var>. We take the same <var>lightSpaceMatrix</var> (used to transform vertices to light space in the depth map stage) and transform the world-space vertex position to light space for use in the fragment shader. +</p> + +<p> + The main fragment shader we'll use to render the scene uses the Blinn-Phong lighting model. Within the fragment shader we then calculate a <var>shadow</var> value that is either <code>1.0</code> when the fragment is in shadow or <code>0.0</code> when not in shadow. The resulting <var>diffuse</var> and <var>specular</var> components are then multiplied by this shadow component. Because shadows are rarely completely dark (due to light scattering) we leave the <var>ambient</var> component out of the shadow multiplications. +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + vec4 FragPosLightSpace; +} fs_in; + +uniform sampler2D diffuseTexture; +uniform sampler2D shadowMap; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +float ShadowCalculation(vec4 fragPosLightSpace) +{ + [...] +} + +void main() +{ + vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; + vec3 normal = normalize(fs_in.Normal); + vec3 lightColor = vec3(1.0); + // ambient + vec3 ambient = 0.15 * color; + // diffuse + vec3 lightDir = normalize(lightPos - fs_in.FragPos); + float diff = max(dot(lightDir, normal), 0.0); + vec3 diffuse = diff * lightColor; + // specular + vec3 viewDir = normalize(viewPos - fs_in.FragPos); + float spec = 0.0; + vec3 halfwayDir = normalize(lightDir + viewDir); + spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); + vec3 specular = spec * lightColor; + // calculate shadow + float shadow = ShadowCalculation(fs_in.FragPosLightSpace); + vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; + + FragColor = vec4(lighting, 1.0); +} +</code></pre> + +<p> + The fragment shader is largely a copy from what we used in the <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting" target="_blank">advanced lighting</a> chapter, but with an added shadow calculation. We declared a function <fun>ShadowCalculation</fun> that does most of the shadow work. At the end of the fragment shader, we multiply the diffuse and specular contributions by the inverse of the <var>shadow</var> component e.g. how much the fragment is <em>not</em> in shadow. This fragment shader takes as extra input the light-space fragment position and the depth map generated from the first render pass. +</p> + +<p> + The first thing to do to check whether a fragment is in shadow, is transform the light-space fragment position in clip-space to normalized device coordinates. When we output a clip-space vertex position to <var>gl_Position</var> in the vertex shader, OpenGL automatically does a perspective divide e.g. transform clip-space coordinates in the range [<code>-w</code>,<code>w</code>] to [<code>-1</code>,<code>1</code>] by dividing the <code>x</code>, <code>y</code> and <code>z</code> component by the vector's <code>w</code> component. As the clip-space <var>FragPosLightSpace</var> is not passed to the fragment shader through <var>gl_Position</var>, we have to do this perspective divide ourselves: +</p> + +<pre><code> +float ShadowCalculation(vec4 fragPosLightSpace) +{ + // perform perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + [...] +} +</code></pre> + +<p> + This returns the fragment's light-space position in the range [<code>-1</code>,<code>1</code>]. +</p> + + <note> + When using an orthographic projection matrix the <code>w</code> component of a vertex remains untouched so this step is actually quite meaningless. However, it is necessary when using perspective projection so keeping this line ensures it works with both projection matrices. + </note> + +<p> + Because the depth from the depth map is in the range [<code>0</code>,<code>1</code>] and we also want to use <var>projCoords</var> to sample from the depth map, we transform the NDC coordinates to the range [<code>0</code>,<code>1</code>]: +</p> + +<pre class="cpp"><code> +projCoords = projCoords * 0.5 + 0.5; +</code></pre> + +<p> + With these projected coordinates we can sample the depth map as the resulting [<code>0</code>,<code>1</code>] coordinates from <var>projCoords</var> directly correspond to the transformed NDC coordinates from the first render pass. This gives us the closest depth from the light's point of view: +</p> + +<pre><code> +float closestDepth = texture(shadowMap, projCoords.xy).r; +</code></pre> + +<p> + To get the current depth at this fragment we simply retrieve the projected vector's <code>z</code> coordinate which equals the depth of this fragment from the light's perspective. +</p> + +<pre><code> +float currentDepth = projCoords.z; +</code></pre> + +<p> + The actual comparison is then simply a check whether <var>currentDepth</var> is higher than <var>closestDepth</var> and if so, the fragment is in shadow: +</p> + +<pre><code> +float shadow = currentDepth > closestDepth ? 1.0 : 0.0; +</code></pre> + +<p> + The complete <fun>ShadowCalculation</fun> function then becomes: +</p> + +<pre><code> +float ShadowCalculation(vec4 fragPosLightSpace) +{ + // perform perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + // transform to [0,1] range + projCoords = projCoords * 0.5 + 0.5; + // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) + float closestDepth = texture(shadowMap, projCoords.xy).r; + // get depth of current fragment from light's perspective + float currentDepth = projCoords.z; + // check whether current frag pos is in shadow + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + + return shadow; +} +</code></pre> + +<p> + Activating this shader, binding the proper textures, and activating the default projection and view matrices in the second render pass should give you a result similar to the image below: +</p> + + <img src="/img/advanced-lighting/shadow_mapping_shadows.png" class="clean" alt="Shadow mapped images, without improvements."/> + +<p> + If you did things right you should indeed see (albeit with quite a few artifacts) shadows on the floor and the cubes. You can find the source code of the demo application <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/3.1.2.shadow_mapping_base/shadow_mapping_base.cpp" target="_blank">here</a>. +</p> + +<h2>Improving shadow maps</h2> +<p> + We managed to get the basics of shadow mapping working, but as you can we're not there yet due to several (clearly visible) artifacts related to shadow mapping we need to fix. We'll focus on fixing these artifacts in the next sections. +</p> + +<h3>Shadow acne</h3> +<p> + It is obvious something is wrong from the previous image. A closer zoom shows us a very obvious Moiré-like pattern: +</p> + + <img src="/img/advanced-lighting/shadow_mapping_acne.png" alt="Image of shadow acne as Moiré pattern with shadow mapping"/> + +<p> + We can see a large part of the floor quad rendered with obvious black lines in an alternating fashion. This shadow mapping artifact is called <def>shadow acne</def> and can be explained by the following image: +</p> + +<img src="/img/advanced-lighting/shadow_mapping_acne_diagram.png" class="clean" alt="Shadow acne explained"/> + +<p> + Because the shadow map is limited by resolution, multiple fragments can sample the same value from the depth map when they're relatively far away from the light source. The image shows the floor where each yellow tilted panel represents a single texel of the depth map. As you can see, several fragments sample the same depth sample. + </p> + +<p> + While this is generally okay, it becomes an issue when the light source looks at an angle towards the surface as in that case the depth map is also rendered from an angle. Several fragments then access the same tilted depth texel while some are above and some below the floor; we get a shadow discrepancy. Because of this, some fragments are considered to be in shadow and some are not, giving the striped pattern from the image. +</p> + +<p> + We can solve this issue with a small little hack called a <def>shadow bias</def> where we simply offset the depth of the surface (or the shadow map) by a small bias amount such that the fragments are not incorrectly considered above the surface. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_acne_bias.png" class="clean" alt="Shadow mapping, with shadow acne fixed using shadow bias."/> + +<p> + With the bias applied, all the samples get a depth smaller than the surface's depth and thus the entire surface is correctly lit without any shadows. We can implement such a bias as follows: +</p> + +<pre><code> +float bias = 0.005; +float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; +</code></pre> + +<p> + A shadow bias of <code>0.005</code> solves the issues of our scene by a large extent, but you can imagine the bias value is highly dependent on the angle between the light source and the surface. If the surface would have a steep angle to the light source, the shadows may still display shadow acne. A more solid approach would be to change the amount of bias based on the surface angle towards the light: something we can solve with the dot product: +</p> + +<pre><code> +float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); +</code></pre> + +<p> + Here we have a maximum bias of <code>0.05</code> and a minimum of <code>0.005</code> based on the surface's normal and light direction. This way, surfaces like the floor that are almost perpendicular to the light source get a small bias, while surfaces like the cube's side-faces get a much larger bias. The following image shows the same scene but now with a shadow bias: +</p> + + + <img src="/img/advanced-lighting/shadow_mapping_with_bias.png" class="clean" alt="Shadow mapped images, with (sloped) shadow bias applied."/> + +<p> + Choosing the correct bias value(s) requires some tweaking as this will be different for each scene, but most of the time it's simply a matter of slowly incrementing the bias until all acne is removed. +</p> + +<h3>Peter panning</h3> +<p> + A disadvantage of using a shadow bias is that you're applying an offset to the actual depth of objects. As a result, the bias may become large enough to see a visible offset of shadows compared to the actual object locations as you can see below (with an exaggerated bias value): +</p> + + <img src="/img/advanced-lighting/shadow_mapping_peter_panning.png" class="clean" alt="Peter panning with shadow mapping implementation"/> + +<p> + This shadow artifact is called <def>peter panning</def> since objects seem slightly <em>detached</em> from their shadows. We can use a little trick to solve most of the peter panning issue by using front face culling when rendering the depth map. You may remember from the <a href="https://learnopengl.com/Advanced-OpenGL/Face-Culling" target="_blank">face culling</a> chapter that OpenGL by default culls back-faces. By telling OpenGL we want to cull front faces during the shadow map stage we're switching that order around. +</p> + +<p> + Because we only need depth values for the depth map it shouldn't matter for solid objects whether we take the depth of their front faces or their back faces. Using their back face depths doesn't give wrong results as it doesn't matter if we have shadows inside objects; we can't see there anyways. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_culling.png" class="clean" alt="Shadow mapping showing how front face culling helps solve peter panning."/> + +<p> + To fix peter panning we cull all front faces during the shadow map generation. Note that you need to enable <var>GL_CULL_FACE</var> first. +</p> + +<pre><code> +<function id='74'>glCullFace</function>(GL_FRONT); +RenderSceneToDepthMap(); +<function id='74'>glCullFace</function>(GL_BACK); // don't forget to reset original culling face +</code></pre> + +<p> + This effectively solves the peter panning issues, but <strong>only for solid</strong> objects that actually have an inside without openings. In our scene for example, this works perfectly fine on the cubes. However, on the floor it won't work as well as culling the front face completely removes the floor from the equation. The floor is a single plane and would thus be completely culled. If one wants to solve peter panning with this trick, care has to be taken to only cull the front faces of objects where it makes sense. +</p> + +<p> + Another consideration is that objects that are close to the shadow receiver (like the distant cube) may still give incorrect results. However, with normal bias values you can generally avoid peter panning. +</p> + +<h3>Over sampling</h3> +<p> + Another visual discrepancy which you may like or dislike is that regions outside the light's visible frustum are considered to be in shadow while they're (usually) not. This happens because projected coordinates outside the light's frustum are higher than <code>1.0</code> and will thus sample the depth texture outside its default range of [<code>0</code>,<code>1</code>]. Based on the texture's wrapping method, we will get incorrect depth results not based on the real depth values from the light source. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_outside_frustum.png" class="clean" alt="Shadow mapping with edges of depth map visible, texture wrapping"/> +<p> + You can see in the image that there is some sort of imaginary region of light, and a large part outside this area is in shadow; this area represents the size of the depth map projected onto the floor. The reason this happens is that we earlier set the depth map's wrapping options to <var>GL_REPEAT</var>. +</p> + +<p> + What we'd rather have is that all coordinates outside the depth map's range have a depth of <code>1.0</code> which as a result means these coordinates will never be in shadow (as no object will have a depth larger than <code>1.0</code>). We can do this by configuring a texture border color and set the depth map's texture wrap options to <var>GL_CLAMP_TO_BORDER</var>: +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); +float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; +<function id='15'>glTexParameter</function>fv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); +</code></pre> + +<p> + Now whenever we sample outside the depth map's [<code>0</code>,<code>1</code>] coordinate range, the <fun>texture</fun> function will always return a depth of <code>1.0</code>, producing a <var>shadow</var> value of <code>0.0</code>. The result now looks more plausible: +</p> + + <img src="/img/advanced-lighting/shadow_mapping_clamp_edge.png" class="clean" alt="Shadow mapping with texture wrapping set to clamp to border color"/> + +<p> + There seems to still be one part showing a dark region. Those are the coordinates outside the far plane of the light's orthographic frustum. You can see that this dark region always occurs at the far end of the light source's frustum by looking at the shadow directions. +</p> + +<p> + A light-space projected fragment coordinate is further than the light's far plane when its <code>z</code> coordinate is larger than <code>1.0</code>. In that case the <var>GL_CLAMP_TO_BORDER</var> wrapping method doesn't work anymore as we compare the coordinate's <code>z</code> component with the depth map values; this always returns true for <code>z</code> larger than <code>1.0</code>. +</p> + +<p> + The fix for this is also relatively easy as we simply force the <var>shadow</var> value to <code>0.0</code> whenever the projected vector's <code>z</code> coordinate is larger than <code>1.0</code>: +</p> + +<pre><code> +float ShadowCalculation(vec4 fragPosLightSpace) +{ + [...] + if(projCoords.z > 1.0) + shadow = 0.0; + + return shadow; +} +</code></pre> + +<p> + Checking the far plane and clamping the depth map to a manually specified border color solves the over-sampling of the depth map. This finally gives us the result we are looking for: +</p> + +<img src="/img/advanced-lighting/shadow_mapping_over_sampling_fixed.png" class="clean" alt="Shadow mapping with over sampling fixed with border clamp to color and far plane fix."/> + +<p> + The result of all this does mean that we only have shadows where the projected fragment coordinates sit inside the depth map range so anything outside the light frustum will have no visible shadows. As games usually make sure this only occurs in the distance it is a much more plausible effect than the obvious black regions we had before. +</p> + +<h2>PCF</h2> +<p> + The shadows right now are a nice addition to the scenery, but it's still not exactly what we want. If you were to zoom in on the shadows the resolution dependency of shadow mapping quickly becomes apparent. +</p> + + <img src="/img/advanced-lighting/shadow_mapping_zoom.png" alt="Zoomed in of shadows with shadow mappign technique shows jagged edges."/> + +<p> + Because the depth map has a fixed resolution, the depth frequently usually spans more than one fragment per texel. As a result, multiple fragments sample the same depth value from the depth map and come to the same shadow conclusions, which produces these jagged blocky edges. +</p> + +<p> + You can reduce these blocky shadows by increasing the depth map resolution, or by trying to fit the light frustum as closely to the scene as possible. +</p> + +<p> + Another (partial) solution to these jagged edges is called PCF, or <def>percentage-closer filtering</def>, which is a term that hosts many different filtering functions that produce <em>softer</em> shadows, making them appear less blocky or hard. The idea is to sample more than once from the depth map, each time with slightly different texture coordinates. For each individual sample we check whether it is in shadow or not. All the sub-results are then combined and averaged and we get a nice soft looking shadow. +</p> + +<p> + One simple implementation of PCF is to simply sample the surrounding texels of the depth map and average the results: +</p> + +<pre><code> +float shadow = 0.0; +vec2 texelSize = 1.0 / textureSize(shadowMap, 0); +for(int x = -1; x &lt;= 1; ++x) +{ + for(int y = -1; y &lt;= 1; ++y) + { + float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; + shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; + } +} +shadow /= 9.0; +</code></pre> + +<p> + Here <fun>textureSize</fun> returns a <code>vec2</code> of the width and height of the given sampler texture at mipmap level <code>0</code>. 1 divided over this returns the size of a single texel that we use to offset the texture coordinates, making sure each new sample samples a different depth value. Here we sample 9 values around the projected coordinate's <code>x</code> and <code>y</code> value, test for shadow occlusion, and finally average the results by the total number of samples taken. +</p> + +<p> + By using more samples and/or varying the <var>texelSize</var> variable you can increase the quality of the soft shadows. Below you can see the shadows with simple PCF applied: +</p> + + <img src="/img/advanced-lighting/shadow_mapping_soft_shadows.png" alt="Soft shadows with PCF using shadow mapping"/> + +<p> + From a distance the shadows look a lot better and less hard. If you zoom in you can still see the resolution artifacts of shadow mapping, but in general this gives good results for most applications. +</p> + +<p> + You can find the complete source code of the example <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/3.1.3.shadow_mapping/shadow_mapping.cpp" target="_blank">here</a>. +</p> + +<p> + There is actually much more to PCF and quite a few techniques to considerably improve the quality of soft shadows, but for the sake of this chapter's length we'll leave that for a later discussion. +</p> + +<h2>Orthographic vs perspective</h2> +<p> + There is a difference between rendering the depth map with an orthographic or a perspective projection matrix. An orthographic projection matrix does not deform the scene with perspective so all view/light rays are parallel. This makes it a great projection matrix for directional lights. A perspective projection matrix however does deform all vertices based on perspective which gives different results. The following image shows the different shadow regions of both projection methods: +</p> + +<img src="/img/advanced-lighting/shadow_mapping_projection.png" class="clean" alt="Shadow mapping difference between orthographic and perspective projection."/> + +<p> + Perspective projections make most sense for light sources that have actual locations, unlike directional lights. Perspective projections are most often used with spotlights and point lights, while orthographic projections are used for directional lights. +</p> + +<p> + Another subtle difference with using a perspective projection matrix is that visualizing the depth buffer will often give an almost completely white result. This happens because with perspective projection the depth is transformed to non-linear depth values with most of its noticeable range close to the near plane. To be able to properly view the depth values as we did with the orthographic projection you first want to transform the non-linear depth values to linear as we discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D depthMap; +uniform float near_plane; +uniform float far_plane; + +float LinearizeDepth(float depth) +{ + float z = depth * 2.0 - 1.0; // Back to NDC + return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane)); +} + +void main() +{ + float depthValue = texture(depthMap, TexCoords).r; + FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective + // FragColor = vec4(vec3(depthValue), 1.0); // orthographic +} +</code></pre> + +<p> + This shows depth values similar to what we've seen with orthographic projection. Note that this is only useful for debugging; the depth checks remain the same with orthographic or projection matrices as the relative depths do not change. +</p> + +<h2>Additional resources</h2> + <ul> + <li><a href="http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/" target="_blank">Tutorial 16 : Shadow mapping</a>: similar shadow mapping tutorial by opengl-tutorial.org with a few extra notes.</li> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial23/tutorial23.html" target="_blank">Shadow Mapping - Part 1</a>: another shadow mapping tutorial by ogldev.</li> + <li><a href="https://www.youtube.com/watch?v=EsccgeUpdsM" target="_blank">How Shadow Mapping Works</a>: a 3-part YouTube tutorial by TheBennyBox on shadow mapping and its implementation.</li> + <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ee416324%28v=vs.85%29.aspx" target="_blank">Common Techniques to Improve Shadow Depth Maps</a>: a great article by Microsoft listing a large number of techniques to improve the quality of shadow maps. +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Advanced-Data.html b/pub/Advanced-OpenGL/Advanced-Data.html @@ -0,0 +1,460 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Advanced Data</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Advanced-Data</h1> +<p> + Throughout most chapters we've been extensively using buffers in OpenGL to store data on the GPU. This chapter we'll briefly discuss a few alternative approaches to managing buffers. +</p> + +<p> + A buffer in OpenGL is, at its core, an object that manages a certain piece of GPU memory and nothing more. We give meaning to a buffer when binding it to a specific <def>buffer target</def>. A buffer is only a vertex array buffer when we bind it to <var>GL_ARRAY_BUFFER</var>, but we could just as easily bind it to <var>GL_ELEMENT_ARRAY_BUFFER</var>. OpenGL internally stores a reference to the buffer per target and, based on the target, processes the buffer differently. +</p> + +<p> + So far we've been filling the buffer's memory by calling <fun><function id='31'>glBufferData</function></fun>, which allocates a piece of GPU memory and adds data into this memory. If we were to pass <code>NULL</code> as its data argument, the function would only allocate memory and not fill it. This is useful if we first want to <em>reserve</em> a specific amount of memory and later come back to this buffer. +</p> + +<p> + Instead of filling the entire buffer with one function call we can also fill specific regions of the buffer by calling <fun><function id='90'>glBufferSubData</function></fun>. This function expects a buffer target, an offset, the size of the data and the actual data as its arguments. What's new with this function is that we can now give an offset that specifies from <em>where</em> we want to fill the buffer. This allows us to insert/update only certain parts of the buffer's memory. Do note that the buffer should have enough allocated memory so a call to <fun><function id='31'>glBufferData</function></fun> is necessary before calling <fun><function id='90'>glBufferSubData</function></fun> on the buffer. +</p> + +<pre><code> +<function id='90'>glBufferSubData</function>(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // Range: [24, 24 + sizeof(data)] +</code></pre> + +<p> + Yet another method for getting data into a buffer is to ask for a pointer to the buffer's memory and directly copy the data in memory yourself. By calling <fun><function id='91'>glMapBuffer</function></fun> OpenGL returns a pointer to the currently bound buffer's memory for us to operate on: +</p> + +<pre><code> +float data[] = { + 0.5f, 1.0f, -0.35f + [...] +}; +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, buffer); +// get pointer +void *ptr = <function id='91'>glMapBuffer</function>(GL_ARRAY_BUFFER, GL_WRITE_ONLY); +// now copy data into memory +memcpy(ptr, data, sizeof(data)); +// make sure to tell OpenGL we're done with the pointer +<function id='92'>glUnmapBuffer</function>(GL_ARRAY_BUFFER); +</code></pre> + +<p> + By telling OpenGL we're finished with the pointer operations via <fun><function id='92'>glUnmapBuffer</function></fun>, OpenGL knows you're done. By unmapping, the pointer becomes invalid and the function returns <var>GL_TRUE</var> if OpenGL was able to map your data successfully to the buffer. +</p> + +<p> + Using <fun><function id='91'>glMapBuffer</function></fun> is useful for directly mapping data to a buffer, without first storing it in temporary memory. Think of directly reading data from file and copying it into the buffer's memory. +</p> + +<h2>Batching vertex attributes</h2> +<p> + Using <fun><function id='30'>glVertexAttribPointer</function></fun> we were able to specify the attribute layout of the vertex array buffer's content. Within the vertex array buffer we <def>interleaved</def> the attributes; that is, we placed the position, normal and/or texture coordinates next to each other in memory for each vertex. Now that we know a bit more about buffers we can take a different approach. +</p> + +<p> + What we could also do is batch all the vector data into large chunks per attribute type instead of interleaving them. Instead of an interleaved layout <code>123123123123</code> we take a batched approach <code>111122223333</code>. +</p> + +<p> + When loading vertex data from file you generally retrieve an array of positions, an array of normals and/or an array of texture coordinates. It may cost some effort to combine these arrays into one large array of interleaved data. Taking the batching approach is then an easier solution that we can easily implement using <fun><function id='90'>glBufferSubData</function></fun>: +</p> + +<pre><code> +float positions[] = { ... }; +float normals[] = { ... }; +float tex[] = { ... }; +// fill buffer +<function id='90'>glBufferSubData</function>(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions); +<function id='90'>glBufferSubData</function>(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals); +<function id='90'>glBufferSubData</function>(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex); +</code></pre> + +<p> + This way we can directly transfer the attribute arrays as a whole into the buffer without first having to process them. We could have also combined them in one large array and fill the buffer right away using <fun><function id='31'>glBufferData</function></fun>, but using <fun><function id='90'>glBufferSubData</function></fun> lends itself perfectly for tasks like these. +</p> +<p> + We'll also have to update the vertex attribute pointers to reflect these changes: +</p> + +<pre><code> +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); +<function id='30'>glVertexAttribPointer</function>(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions))); +<function id='30'>glVertexAttribPointer</function>( + 2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals))); +</code></pre> + +<p> + Note that the <code>stride</code> parameter is equal to the size of the vertex attribute, since the next vertex attribute vector can be found directly after its 3 (or 2) components. +</p> + +<p> + This gives us yet another approach of setting and specifying vertex attributes. Using either approach is feasible, it is mostly a more organized way to set vertex attributes. However, the interleaved approach is still the recommended approach as the vertex attributes for each vertex shader run are then closely aligned in memory. +</p> + +<h2>Copying buffers</h2> +<p> + Once your buffers are filled with data you may want to share that data with other buffers or perhaps copy the buffer's content into another buffer. The function <fun><function id='93'>glCopyBufferSubData</function></fun> allows us to copy the data from one buffer to another buffer with relative ease. The function's prototype is as follows: +</p> + +<pre><code> +void <function id='93'>glCopyBufferSubData</function>(GLenum readtarget, GLenum writetarget, GLintptr readoffset, + GLintptr writeoffset, GLsizeiptr size); +</code></pre> + +<p> + The <code>readtarget</code> and <code>writetarget</code> parameters expect to give the buffer targets that we want to copy from and to. We could for example copy from a <var>VERTEX_ARRAY_BUFFER</var> buffer to a <var>VERTEX_ELEMENT_ARRAY_BUFFER</var> buffer by specifying those buffer targets as the read and write targets respectively. The buffers currently bound to those buffer targets will then be affected. +</p> + +<p> + But what if we wanted to read and write data into two different buffers that are both vertex array buffers? We can't bind two buffers at the same time to the same buffer target. For this reason, and this reason alone, OpenGL gives us two more buffer targets called <var>GL_COPY_READ_BUFFER</var> and <var>GL_COPY_WRITE_BUFFER</var>. We then bind the buffers of our choice to these new buffer targets and set those targets as the <code>readtarget</code> and <code>writetarget</code> argument. +</p> + +<p> + <fun><function id='93'>glCopyBufferSubData</function></fun> then reads data of a given <code>size</code> from a given <code>readoffset</code> and writes it into the <code>writetarget</code> buffer at <code>writeoffset</code>. An example of copying the content of two vertex array buffers is shown below: +</p> + +<pre><code> +<function id='32'>glBindBuffer</function>(GL_COPY_READ_BUFFER, vbo1); +<function id='32'>glBindBuffer</function>(GL_COPY_WRITE_BUFFER, vbo2); +<function id='93'>glCopyBufferSubData</function>(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, 8 * sizeof(float)); +</code></pre> + +<p> + We could've also done this by only binding the <code>writetarget</code> buffer to one of the new buffer target types: +</p> + +<pre><code> +float vertexData[] = { ... }; +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, vbo1); +<function id='32'>glBindBuffer</function>(GL_COPY_WRITE_BUFFER, vbo2); +<function id='93'>glCopyBufferSubData</function>(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, 8 * sizeof(float)); +</code></pre> + + +<p> + With some extra knowledge about how to manipulate buffers we can already use them in more interesting ways. The further you get in OpenGL, the more useful these new buffer methods start to become. In the <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL" target="_blank">next</a> chapter, where we'll discuss <def>uniform buffer objects</def>, we'll make good use of <fun><function id='90'>glBufferSubData</function></fun>. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Advanced-GLSL.html b/pub/Advanced-OpenGL/Advanced-GLSL.html @@ -0,0 +1,952 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Advanced GLSL</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Advanced-GLSL</h1> +<p> + This chapter won't really show you super advanced cool new features that give an enormous boost to your scene's visual quality. This chapter goes more or less into some interesting aspects of GLSL and some nice tricks that may help you in your future endeavors. Basically some <em>good to knows</em> and <em>features that may make your life easier</em> when creating OpenGL applications in combination with GLSL. +</p> + +<p> + We'll discuss some interesting <def>built-in variables</def>, new ways to organize shader input and output, and a very useful tool called <def>uniform buffer objects</def>. +</p> + +<h1>GLSL's built-in variables</h1> +<p> + Shaders are extremely pipelined, if we need data from any other source outside of the current shader we'll have to pass data around. We learned to do this via vertex attributes, uniforms, and samplers. There are however a few extra variables defined by GLSL prefixed with <code>gl_</code> that give us an extra means to gather and/or write data. We've already seen two of them in the chapters so far: <var>gl_Position</var> that is the output vector of the vertex shader, and the fragment shader's <var>gl_FragCoord</var>. +</p> + +<p> + We'll discuss a few interesting built-in input and output variables that are built-in in GLSL and explain how they may benefit us. Note that we won't discuss all built-in variables that exist in GLSL so if you want to see all built-in variables you can check OpenGL's <a href="https://www.khronos.org/opengl/wiki/Built-in_Variable_(GLSL)" target="_blank">wiki</a>. +</p> + +<h2>Vertex shader variables</h2> +<p> + We've already seen <var>gl_Position</var> which is the clip-space output position vector of the vertex shader. Setting <var>gl_Position</var> in the vertex shader is a strict requirement if you want to render anything on the screen. Nothing we haven't seen before. +</p> + +<h3>gl_PointSize</h3> +<p> + One of the render primitives we're able to choose from is <var>GL_POINTS</var> in which case each single vertex is a primitive and rendered as a point. It is possible to set the size of the points being rendered via OpenGL's <fun>glPointSize</fun> function, but we can also influence this value in the vertex shader. +</p> + +<p> + One output variable defined by GLSL is called <var>gl_PointSize</var> that is a <fun>float</fun> variable where you can set the point's width and height in pixels. By setting the point's size in the vertex shader we get per-vertex control over this point's dimensions. +</p> + +<p> + Influencing the point sizes in the vertex shader is disabled by default, but if you want to enable this you'll have to enable OpenGL's <var>GL_PROGRAM_POINT_SIZE</var>: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_PROGRAM_POINT_SIZE); +</code></pre> + +<p> + A simple example of influencing point sizes is by setting the point size equal to the clip-space position's z value which is equal to the vertex's distance to the viewer. The point size should then increase the further we are from the vertices as the viewer. +</p> + +<pre><code> +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + gl_PointSize = gl_Position.z; +} +</code></pre> + +<p> + The result is that the points we've drawn are rendered larger the more we move away from them: +</p> + +<img src="/img/advanced/advanced_glsl_pointsize.png" class="clean" alt="Points in OpenGL drawn with their gl_PointSize influenced in the vertex shader"/> + +<p> + You can imagine that varying the point size per vertex is interesting for techniques like particle generation. +</p> + +<h3>gl_VertexID</h3> +<p> + The <var>gl_Position</var> and <var>gl_PointSize</var> are <em>output variables</em> since their value is read as output from the vertex shader; we can influence the result by writing to them. The vertex shader also gives us an interesting <em>input variable</em>, that we can only read from, called <var>gl_VertexID</var>. +</p> + +<p> + The integer variable <var>gl_VertexID</var> holds the current ID of the vertex we're drawing. When doing <em>indexed rendering</em> (with <fun><function id='2'>glDrawElements</function></fun>) this variable holds the current index of the vertex we're drawing. When drawing without indices (via <fun><function id='1'>glDrawArrays</function></fun>) this variable holds the number of the currently processed vertex since the start of the render call. +</p> + +<h2>Fragment shader variables</h2> +<p> + Within the fragment shader we also have access to some interesting variables. GLSL gives us two interesting input variables called <var>gl_FragCoord</var> and <var>gl_FrontFacing</var>. +</p> + +<h3>gl_FragCoord</h3> +<p> + We've seen the <var>gl_FragCoord</var> a couple of times before during the discussion of depth testing, because the <code>z</code> component of the <var>gl_FragCoord</var> vector is equal to the depth value of that particular fragment. However, we can also use the x and y component of that vector for some interesting effects. +</p> + +<p> + The <var>gl_FragCoord</var>'s <code>x</code> and <code>y</code> component are the window- or screen-space coordinates of the fragment, originating from the bottom-left of the window. We specified a render window of 800x600 with <fun><function id='22'>glViewport</function></fun> so the screen-space coordinates of the fragment will have <code>x</code> values between 0 and 800, and <code>y</code> values between 0 and 600. +</p> + +<p> + Using the fragment shader we could calculate a different color value based on the screen coordinate of the fragment. A common usage for the <var>gl_FragCoord</var> variable is for comparing visual output of different fragment calculations, as usually seen in tech demos. We could for example split the screen in two by rendering one output to the left side of the window and another output to the right side of the window. An example fragment shader that outputs a different color based on the fragment's screen coordinates is given below: +</p> + +<pre><code> +void main() +{ + if(gl_FragCoord.x &lt; 400) + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + else + FragColor = vec4(0.0, 1.0, 0.0, 1.0); +} +</code></pre> + +<p> + Because the width of the window is equal to 800, whenever a pixel's x-coordinate is less than 400 it must be at the left side of the window and we'll give that fragment a different color. +</p> + +<img src="/img/advanced/advanced_glsl_fragcoord.png" class="clean" alt="Cube in OpenGL drawn with 2 colors using gl_FragCoord"/> + +<p> + We can now calculate two completely different fragment shader results and display each of them on a different side of the window. This is great for testing out different lighting techniques for example. +</p> + +<h3>gl_FrontFacing</h3> +<p> + Another interesting input variable in the fragment shader is the <var>gl_FrontFacing</var> variable. In the <a href="https://learnopengl.com/Advanced-OpenGL/Face-culling" target="_blank">face culling</a> chapter we mentioned that OpenGL is able to figure out if a face is a front or back face due to the winding order of the vertices. The <var>gl_FrontFacing</var> variable tells us if the current fragment is part of a front-facing or a back-facing face. We could, for example, decide to output different colors for all back faces. +</p> + +<p> + The <var>gl_FrontFacing</var> variable is a <fun>bool</fun> that is <code>true</code> if the fragment is part of a front face and <code>false</code> otherwise. We could create a cube this way with a different texture on the inside than on the outside: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D frontTexture; +uniform sampler2D backTexture; + +void main() +{ + if(gl_FrontFacing) + FragColor = texture(frontTexture, TexCoords); + else + FragColor = texture(backTexture, TexCoords); +} +</code></pre> + +<p> + If we take a peek inside the container we can now see a different texture being used. +</p> + +<img src="/img/advanced/advanced_glsl_frontfacing.png" class="clean" alt="OpenGL container using two different textures via gl_FrontFacing"/> + +<p> + Note that if you enabled face culling you won't be able to see any faces inside the container and using <var>gl_FrontFacing</var> would then be pointless. +</p> + +<h3>gl_FragDepth</h3> +<p> + The input variable <var>gl_FragCoord</var> is an input variable that allows us to read screen-space coordinates and get the depth value of the current fragment, but it is a <def>read-only</def> variable. We can't influence the screen-space coordinates of the fragment, but it is possible to set the depth value of the fragment. GLSL gives us an output variable called <var>gl_FragDepth</var> that we can use to manually set the depth value of the fragment within the shader. +</p> + +<p> + To set the depth value in the shader we write any value between <code>0.0</code> and <code>1.0</code> to the output variable: +</p> + +<pre><code> +gl_FragDepth = 0.0; // this fragment now has a depth value of 0.0 +</code></pre> + +<p> + If the shader does not write anything to <var>gl_FragDepth</var>, the variable will automatically take its value from <code>gl_FragCoord.z</code>. +</p> + +<p> + Setting the depth value manually has a major disadvantage however. That is because OpenGL disables <def>early depth testing</def> (as discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter) as soon as we write to <var>gl_FragDepth</var> in the fragment shader. It is disabled, because OpenGL cannot know what depth value the fragment will have <em>before</em> we run the fragment shader, since the fragment shader may actually change this value. +</p> + +<p> + By writing to <var>gl_FragDepth</var> you should take this performance penalty into consideration. From OpenGL 4.2 however, we can still sort of mediate between both sides by redeclaring the <var>gl_FragDepth</var> variable at the top of the fragment shader with a <def>depth condition</def>: +</p> + +<pre><code> +layout (depth_&ltcondition&gt;) out float gl_FragDepth; +</code></pre> + +<p> + This <code>condition</code> can take the following values: +</p> + +<table> + <tr> + <th>Condition</th> + <th>Description</th> + </tr> + <tr> + <td><code>any</code></td> + <td>The default value. Early depth testing is disabled.</td> + </tr> + <tr> + <td><code>greater</code></td> + <td>You can only make the depth value larger compared to <code>gl_FragCoord.z</code>.</td> + </tr> + <tr> + <td><code>less</code></td> + <td>You can only make the depth value smaller compared to <code>gl_FragCoord.z</code>.</td> + </tr> + <tr> + <td><code>unchanged</code></td> + <td>If you write to <code>gl_FragDepth</code>, you will write exactly <code>gl_FragCoord.z</code>.</td> + </tr> +</table> + +<p> + By specifying <code>greater</code> or <code>less</code> as the depth condition, OpenGL can make the assumption that you'll only write depth values larger or smaller than the fragment's depth value. This way OpenGL is still able to do early depth testing when the depth buffer value is part of the other direction of <code>gl_FragCoord.z</code>. +</p> + +<p> + An example of where we increase the depth value in the fragment shader, but still want to preserve some of the early depth testing is shown in the fragment shader below: +</p> + +<pre><code> +#version 420 core // note the GLSL version! +out vec4 FragColor; +layout (depth_greater) out float gl_FragDepth; + +void main() +{ + FragColor = vec4(1.0); + gl_FragDepth = gl_FragCoord.z + 0.1; +} +</code></pre> + +<p> + Do note that this feature is only available from OpenGL version 4.2 or higher. +</p> + +<h1>Interface blocks</h1> +<p> + So far, every time we sent data from the vertex to the fragment shader we declared several matching input/output variables. Declaring these one at a time is the easiest way to send data from one shader to another, but as applications become larger you probably want to send more than a few variables over. +</p> + +<p> + To help us organize these variables GLSL offers us something called <def>interface blocks</def> that allows us to group variables together. The declaration of such an interface block looks a lot like a <fun>struct</fun> declaration, except that it is now declared using an <fun>in</fun> or <fun>out</fun> keyword based on the block being an input or an output block. +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoords; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +out VS_OUT +{ + vec2 TexCoords; +} vs_out; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + vs_out.TexCoords = aTexCoords; +} +</code></pre> + +<p> + This time we declared an interface block called <var>vs_out</var> that groups together all the output variables we want to send to the next shader. This is kind of a trivial example, but you can imagine that this helps organize your shaders' inputs/outputs. It is also useful when we want to group shader input/output into arrays as we'll see in the <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader" target="_blank">next</a> chapter about geometry shaders. +</p> + +<p> + Then we also need to declare an input interface block in the next shader which is the fragment shader. The <def>block name</def> (<fun>VS_OUT</fun>) should be the same in the fragment shader, but the <def>instance name</def> (<var>vs_out</var> as used in the vertex shader) can be anything we like - avoiding confusing names like <var>vs_out</var> for a fragment struct containing input values. +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in VS_OUT +{ + vec2 TexCoords; +} fs_in; + +uniform sampler2D texture; + +void main() +{ + FragColor = texture(texture, fs_in.TexCoords); +} +</code></pre> + +<p> + As long as both interface block names are equal, their corresponding input and output is matched together. This is another useful feature that helps organize your code and proves useful when crossing between certain shader stages like the geometry shader. +</p> + +<h1>Uniform buffer objects</h1> +<p> + We've been using OpenGL for quite a while now and learned some pretty cool tricks, but also a few annoyances. For example, when using more than one shader we continuously have to set uniform variables where most of them are exactly the same for each shader. +</p> + +<p> + OpenGL gives us a tool called <def>uniform buffer objects</def> that allow us to declare a set of <em>global</em> uniform variables that remain the same over any number of shader programs. When using uniform buffer objects we set the relevant uniforms only <strong>once</strong> in fixed GPU memory. We do still have to manually set the uniforms that are unique per shader. Creating and configuring a uniform buffer object requires a bit of work though. +</p> + +<p> + Because a uniform buffer object is a buffer like any other buffer we can create one via <fun><function id='12'>glGenBuffers</function></fun>, bind it to the <var>GL_UNIFORM_BUFFER</var> buffer target and store all the relevant uniform data into the buffer. There are certain rules as to how the data for uniform buffer objects should be stored and we'll get to that later. First, we'll take a simple vertex shader and store our <var>projection</var> and <var>view</var> matrix in a so called <def>uniform block</def>: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +layout (std140) uniform Matrices +{ + mat4 projection; + mat4 view; +}; + +uniform mat4 model; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + In most of our samples we set a projection and view uniform matrix every frame for each shader we're using. This is a perfect example of where uniform buffer objects become useful since now we only have to store these matrices once. +</p> + +<p> + Here we declared a uniform block called <var>Matrices</var> that stores two 4x4 matrices. Variables in a uniform block can be directly accessed without the block name as a prefix. Then we store these matrix values in a buffer somewhere in the OpenGL code and each shader that declares this uniform block has access to the matrices. +</p> + +<p> + You're probably wondering right now what the <code>layout</code> <code>(std140)</code> statement means. What this says is that the currently defined uniform block uses a specific memory layout for its content; this statement sets the <def>uniform block layout</def>. +</p> + +<h2>Uniform block layout</h2> +<p> + The content of a uniform block is stored in a buffer object, which is effectively nothing more than a reserved piece of global GPU memory. Because this piece of memory holds no information on what kind of data it holds, we need to tell OpenGL what parts of the memory correspond to which uniform variables in the shader. +</p> + +<p> + Imagine the following uniform block in a shader: +</p> + +<pre><code> +layout (std140) uniform ExampleBlock +{ + float value; + vec3 vector; + mat4 matrix; + float values[3]; + bool boolean; + int integer; +}; +</code></pre> + +<p> + What we want to know is the size (in bytes) and the offset (from the start of the block) of each of these variables so we can place them in the buffer in their respective order. The size of each of the elements is clearly stated in OpenGL and directly corresponds to C++ data types; vectors and matrices being (large) arrays of floats. What OpenGL doesn't clearly state is the <def>spacing</def> between the variables. This allows the hardware to position or pad variables as it sees fit. The hardware is able to place a <fun>vec3</fun> adjacent to a <fun>float</fun> for example. Not all hardware can handle this and pads the <fun>vec3</fun> to an array of 4 floats before appending the <fun>float</fun>. A great feature, but inconvenient for us. +</p> + +<p> + By default, GLSL uses a uniform memory layout called a <def>shared</def> layout - shared because once the offsets are defined by the hardware, they are consistently <em>shared</em> between multiple programs. With a shared layout GLSL is allowed to reposition the uniform variables for optimization as long as the variables' order remains intact. Because we don't know at what offset each uniform variable will be we don't know how to precisely fill our uniform buffer. We can query this information with functions like <fun>glGetUniformIndices</fun>, but that's not the approach we're going to take in this chapter. +</p> + +<p> + While a shared layout gives us some space-saving optimizations, we'd need to query the offset for each uniform variable which translates to a lot of work. The general practice however is to not use the shared layout, but to use the <def>std140</def> layout. The std140 layout <strong>explicitly</strong> states the memory layout for each variable type by standardizing their respective offsets governed by a set of rules. Since this is standardized we can manually figure out the offsets for each variable. +</p> + +<p> + Each variable has a <def>base alignment</def> equal to the space a variable takes (including padding) within a uniform block using the std140 layout rules. For each variable, we calculate its <def>aligned offset</def>: the byte offset of a variable from the start of the block. The aligned byte offset of a variable <strong>must</strong> be equal to a multiple of its base alignment. This is a bit of a mouthful, but we'll get to see some examples soon enough to clear things up. +</p> + +<p> + The exact layout rules can be found at OpenGL's uniform buffer specification <a href="http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt" target="_blank">here</a>, but we'll list the most common rules below. Each variable type in GLSL such as <fun>int</fun>, <fun>float</fun> and <fun>bool</fun> are defined to be four-byte quantities with each entity of 4 bytes represented as <code>N</code>. +</p> + +<table> + <tr> + <th>Type</th> + <th>Layout rule</th> + </tr> + + <tr> + <td>Scalar e.g. <fun>int</fun> or <fun>bool</fun></td> + <td>Each scalar has a base alignment of N.</td> + </tr> + <tr> + <td>Vector</td> + <td>Either 2N or 4N. This means that a <fun>vec3</fun> has a base alignment of 4N.</td> + </tr> + <tr> + <td>Array of scalars or vectors</td> + <td>Each element has a base alignment equal to that of a <fun>vec4</fun>.</td> + </tr> + <tr> + <td>Matrices</td> + <td>Stored as a large array of column vectors, where each of those vectors has a base alignment of <fun>vec4</fun>.</td> + </tr> + <tr> + <td>Struct</td> + <td>Equal to the computed size of its elements according to the previous rules, but padded to a multiple of the size of a <fun>vec4</fun>.</td> + </tr> +</table> + +<p> + Like most of OpenGL's specifications it's easier to understand with an example. We're taking the uniform block called <var>ExampleBlock</var> we introduced earlier and calculate the aligned offset for each of its members using the std140 layout: +</p> + +<pre><code> +layout (std140) uniform ExampleBlock +{ + // base alignment // aligned offset + float value; // 4 // 0 + vec3 vector; // 16 // 16 (offset must be multiple of 16 so 4->16) + mat4 matrix; // 16 // 32 (column 0) + // 16 // 48 (column 1) + // 16 // 64 (column 2) + // 16 // 80 (column 3) + float values[3]; // 16 // 96 (values[0]) + // 16 // 112 (values[1]) + // 16 // 128 (values[2]) + bool boolean; // 4 // 144 + int integer; // 4 // 148 +}; +</code></pre> + +<p> + As an exercise, try to calculate the offset values yourself and compare them to this table. With these calculated offset values, based on the rules of the std140 layout, we can fill the buffer with data at the appropriate offsets using functions like <fun><function id='90'>glBufferSubData</function></fun>. While not the most efficient, the std140 layout does guarantee us that the memory layout remains the same over each program that declared this uniform block. +</p> + +<p> + By adding the statement <code>layout</code> <code>(std140)</code> in the definition of the uniform block we tell OpenGL that this uniform block uses the std140 layout. There are two other layouts to choose from that require us to query each offset before filling the buffers. We've already seen the <code>shared</code> layout, with the other remaining layout being <code>packed</code>. When using the <code>packed</code> layout, there is no guarantee that the layout remains the same between programs (not shared) because it allows the compiler to optimize uniform variables away from the uniform block which may differ per shader. +</p> + +<h2>Using uniform buffers</h2> +<p> + We've defined uniform blocks and specified their memory layout, but we haven't discussed how to actually use them yet. +</p> + +<p> + First, we need to create a uniform buffer object which is done via the familiar <fun><function id='12'>glGenBuffers</function></fun>. Once we have a buffer object we bind it to the <var>GL_UNIFORM_BUFFER</var> target and allocate enough memory by calling <fun><function id='31'>glBufferData</function></fun>. +</p> + +<pre><code> +unsigned int uboExampleBlock; +<function id='12'>glGenBuffers</function>(1, &uboExampleBlock); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, uboExampleBlock); +<function id='31'>glBufferData</function>(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // allocate 152 bytes of memory +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, 0); +</code></pre> + +<p> + Now whenever we want to update or insert data into the buffer, we bind to <var>uboExampleBlock</var> and use <fun><function id='90'>glBufferSubData</function></fun> to update its memory. We only have to update this uniform buffer once, and all shaders that use this buffer now use its updated data. But, how does OpenGL know what uniform buffers correspond to which uniform blocks? +</p> + +<p> + In the OpenGL context there is a number of <def>binding points</def> defined where we can link a uniform buffer to. Once we created a uniform buffer we link it to one of those binding points and we also link the uniform block in the shader to the same binding point, effectively linking them together. The following diagram illustrates this: +</p> + +<img src="/img/advanced/advanced_glsl_binding_points.png" class="clean" alt="Diagram of uniform binding points in OpenGL"/> + +<p> + As you can see we can bind multiple uniform buffers to different binding points. Because shader A and shader B both have a uniform block linked to the same binding point <code>0</code>, their uniform blocks share the same uniform data found in <var>uboMatrices</var>; a requirement being that both shaders defined the same <var>Matrices</var> uniform block. +</p> + +<p> + To set a shader uniform block to a specific binding point we call <fun><function id='95'><function id='44'>glUniform</function>BlockBinding</function></fun> that takes a program object, a uniform block index, and the binding point to link to. The <def>uniform block index</def> is a location index of the defined uniform block in the shader. This can be retrieved via a call to <fun><function id='94'>glGetUniformBlockIndex</function></fun> that accepts a program object and the name of the uniform block. We can set the <var>Lights</var> uniform block from the diagram to binding point <code>2</code> as follows: +</p> + +<pre><code> +unsigned int lights_index = <function id='94'>glGetUniformBlockIndex</function>(shaderA.ID, "Lights"); +<function id='95'><function id='44'>glUniform</function>BlockBinding</function>(shaderA.ID, lights_index, 2); +</code></pre> + +<p> + Note that we have to repeat this process for <strong>each</strong> shader. +</p> + +<note> + From OpenGL version 4.2 and onwards it is also possible to store the binding point of a uniform block explicitly in the shader by adding another layout specifier, saving us the calls to <fun><function id='94'>glGetUniformBlockIndex</function></fun> and <fun><function id='95'><function id='44'>glUniform</function>BlockBinding</function></fun>. The following code sets the binding point of the <var>Lights</var> uniform block explicitly: +<pre class="cpp"><code> +layout(std140, binding = 2) uniform Lights { ... }; +</code></pre> +</note> + +<p> + Then we also need to bind the uniform buffer object to the same binding point and this can be accomplished with either <fun><function id='96'><function id='32'>glBindBuffer</function>Base</function></fun> or <fun><function id='97'><function id='32'>glBindBuffer</function>Range</function></fun>. +</p> + +<pre><code> +<function id='96'><function id='32'>glBindBuffer</function>Base</function>(GL_UNIFORM_BUFFER, 2, uboExampleBlock); +// or +<function id='97'><function id='32'>glBindBuffer</function>Range</function>(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152); +</code></pre> + +<p> + The function <fun><function id='96'><function id='32'>glBindbuffer</function>Base</function></fun> expects a target, a binding point index and a uniform buffer object. This function links <var>uboExampleBlock</var> to binding point <code>2</code>; from this point on, both sides of the binding point are linked. You can also use <fun><function id='97'><function id='32'>glBindBuffer</function>Range</function></fun> that expects an extra offset and size parameter - this way you can bind only a specific range of the uniform buffer to a binding point. Using <fun><function id='97'><function id='32'>glBindBuffer</function>Range</function></fun> you could have multiple different uniform blocks linked to a single uniform buffer object. +</p> + +<p> + Now that everything is set up, we can start adding data to the uniform buffer. We could add all the data as a single byte array, or update parts of the buffer whenever we feel like it using <fun><function id='90'>glBufferSubData</function></fun>. To update the uniform variable <var>boolean</var> we could update the uniform buffer object as follows: +</p> + +<pre><code> +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, uboExampleBlock); +int b = true; // bools in GLSL are represented as 4 bytes, so we store it in an integer +<function id='90'>glBufferSubData</function>(GL_UNIFORM_BUFFER, 144, 4, &b); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, 0); +</code></pre> + +<p> + And the same procedure applies for all the other uniform variables inside the uniform block, but with different range arguments. +</p> + +<h2>A simple example</h2> +<p> + So let's demonstrate a real example of uniform buffer objects. If we look back at all the previous code samples we've continually been using 3 matrices: the projection, view and model matrix. Of all those matrices, only the model matrix changes frequently. If we have multiple shaders that use this same set of matrices, we'd probably be better off using uniform buffer objects. +</p> + +<p> + We're going to store the projection and view matrix in a uniform block called <var>Matrices</var>. We're not going to store the model matrix in there since the model matrix tends to change frequently between shaders, so we wouldn't really benefit from uniform buffer objects. +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +layout (std140) uniform Matrices +{ + mat4 projection; + mat4 view; +}; +uniform mat4 model; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + Not much going on here, except that we now use a uniform block with a std140 layout. What we're going to do in our sample application is display 4 cubes where each cube is displayed with a different shader program. Each of the 4 shader programs uses the same vertex shader, but has a unique fragment shader that only outputs a single color that differs per shader. +</p> + +<p> + First, we set the uniform block of the vertex shaders equal to binding point <code>0</code>. Note that we have to do this for each shader: +</p> + +<pre><code> +unsigned int uniformBlockIndexRed = <function id='94'>glGetUniformBlockIndex</function>(shaderRed.ID, "Matrices"); +unsigned int uniformBlockIndexGreen = <function id='94'>glGetUniformBlockIndex</function>(shaderGreen.ID, "Matrices"); +unsigned int uniformBlockIndexBlue = <function id='94'>glGetUniformBlockIndex</function>(shaderBlue.ID, "Matrices"); +unsigned int uniformBlockIndexYellow = <function id='94'>glGetUniformBlockIndex</function>(shaderYellow.ID, "Matrices"); + +<function id='95'><function id='44'>glUniform</function>BlockBinding</function>(shaderRed.ID, uniformBlockIndexRed, 0); +<function id='95'><function id='44'>glUniform</function>BlockBinding</function>(shaderGreen.ID, uniformBlockIndexGreen, 0); +<function id='95'><function id='44'>glUniform</function>BlockBinding</function>(shaderBlue.ID, uniformBlockIndexBlue, 0); +<function id='95'><function id='44'>glUniform</function>BlockBinding</function>(shaderYellow.ID, uniformBlockIndexYellow, 0); +</code></pre> + +<p> + Next we create the actual uniform buffer object and bind that buffer to binding point <code>0</code>: +</p> + +<pre><code> +unsigned int uboMatrices +<function id='12'>glGenBuffers</function>(1, &uboMatrices); + +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, uboMatrices); +<function id='31'>glBufferData</function>(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, 0); + +<function id='97'><function id='32'>glBindBuffer</function>Range</function>(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4)); +</code></pre> + +<p> + First we allocate enough memory for our buffer which is equal to 2 times the size of <fun>glm::mat4</fun>. The size of GLM's matrix types correspond directly to <fun>mat4</fun> in GLSL. Then we link a specific range of the buffer, in this case the entire buffer, to binding point <code>0</code>. +</p> + +<p> + Now all that's left to do is fill the buffer. If we keep the <em>field of view</em> value constant of the projection matrix (so no more camera zoom) we only have to update it once in our application - this means we only have to insert this into the buffer only once as well. Because we already allocated enough memory in the buffer object we can use <fun><function id='90'>glBufferSubData</function></fun> to store the projection matrix before we enter the render loop: +</p> + +<pre><code> +glm::mat4 projection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(45.0f), (float)width/(float)height, 0.1f, 100.0f); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, uboMatrices); +<function id='90'>glBufferSubData</function>(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection)); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, 0); +</code></pre> + +<p> + Here we store the first half of the uniform buffer with the projection matrix. Then before we render the objects each frame we update the second half of the buffer with the view matrix: +</p> + +<pre><code> +glm::mat4 view = camera.GetViewMatrix(); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, uboMatrices); +<function id='90'>glBufferSubData</function>(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view)); +<function id='32'>glBindBuffer</function>(GL_UNIFORM_BUFFER, 0); +</code></pre> + +<p> + And that's it for uniform buffer objects. Each vertex shader that contains a <var>Matrices</var> uniform block will now contain the data stored in <var>uboMatrices</var>. So if we now were to draw 4 cubes using 4 different shaders, their projection and view matrix should be the same: +</p> + +<pre><code> +<function id='27'>glBindVertexArray</function>(cubeVAO); +shaderRed.use(); +glm::mat4 model = glm::mat4(1.0f); +model = <function id='55'>glm::translate</function>(model, glm::vec3(-0.75f, 0.75f, 0.0f)); // move top-left +shaderRed.setMat4("model", model); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +// ... draw Green Cube +// ... draw Blue Cube +// ... draw Yellow Cube +</code></pre> + +<p> + The only uniform we still need to set is the <var>model</var> uniform. Using uniform buffer objects in a scenario like this saves us from quite a few uniform calls per shader. The result looks something like this: +</p> + +<img src="/img/advanced/advanced_glsl_uniform_buffer_objects.png" class="clean" alt="Image of 4 cubes with their uniforms set via OpenGL's uniform buffer objects"/> + +<p> + Each of the cubes is moved to one side of the window by translating the model matrix and, thanks to the different fragment shaders, their colors differ per object. This is a relatively simple scenario of where we could use uniform buffer objects, but any large rendering application can have over hundreds of shader programs active which is where uniform buffer objects really start to shine. +</p> + +<p> + You can find the full source code of the uniform example application <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/8.advanced_glsl_ubo/advanced_glsl_ubo.cpp" target="_blank">here</a>. +</p> + +<p> + Uniform buffer objects have several advantages over single uniforms. First, setting a lot of uniforms at once is faster than setting multiple uniforms one at a time. Second, if you want to change the same uniform over several shaders, it is much easier to change a uniform once in a uniform buffer. One last advantage that is not immediately apparent is that you can use a lot more uniforms in shaders using uniform buffer objects. OpenGL has a limit to how much uniform data it can handle which can be queried with <var>GL_MAX_VERTEX_UNIFORM_COMPONENTS</var>. When using uniform buffer objects, this limit is much higher. So whenever you reach a maximum number of uniforms (when doing skeletal animation for example) there's always uniform buffer objects. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Anti-Aliasing.html b/pub/Advanced-OpenGL/Anti-Aliasing.html @@ -0,0 +1,608 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Anti Aliasing</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Anti-Aliasing</h1> +<p> + Somewhere in your adventurous rendering journey you probably came across some jagged saw-like patterns along the edges of your models. The reason these <def>jagged edges</def> appear is due to how the rasterizer transforms the vertex data into actual fragments behind the scene. An example of what these jagged edges look like can already be seen when drawing a simple cube: +</p> + +<img src="/img/advanced/anti_aliasing_aliasing.png" class="clean" alt="Container with visible aliasing"/> + +<p> + While not immediately visible, if you take a closer look at the edges of the cube you'll see a jagged pattern. If we zoom in you'd see the following: +</p> + +<img src="/img/advanced/anti_aliasing_zoomed.png" alt="Zoomed in on contanier with visible aliasing"/> + +<p> + This is clearly not something we want in a final version of an application. This effect, of clearly seeing the pixel formations an edge is composed of, is called <def>aliasing</def>. There are quite a few techniques out there called <def>anti-aliasing</def> techniques that fight this aliasing behavior by producing <em>smoother</em> edges. +</p> + +<p> + At first we had a technique called <def>super sample anti-aliasing</def> (SSAA) that temporarily uses a much higher resolution render buffer to render the scene in (super sampling). Then when the full scene is rendered, the resolution is downsampled back to the normal resolution. This <em>extra</em> resolution was used to prevent these jagged edges. While it did provide us with a solution to the aliasing problem, it came with a major performance drawback since we have to draw <strong>a lot</strong> more fragments than usual. This technique therefore only had a short glory moment. +</p> + +<p> + This technique did give birth to a more modern technique called <def>multisample anti-aliasing</def> or MSAA that borrows from the concepts behind SSAA while implementing a much more efficient approach. In this chapter we'll be extensively discussing this MSAA technique that is built-in in OpenGL. +</p> + +<h2>Multisampling</h2> +<p> + To understand what multisampling is and how it works into solving the aliasing problem we first need to delve a bit further into the inner workings of OpenGL's rasterizer. +</p> + +<p> + The rasterizer is the combination of all algorithms and processes that sit between your final processed vertices and the fragment shader. The rasterizer takes all vertices belonging to a single primitive and transforms this to a set of fragments. Vertex coordinates can theoretically have any coordinate, but fragments can't since they are bound by the resolution of your screen. There will almost never be a one-on-one mapping between vertex coordinates and fragments, so the rasterizer has to determine in some way what fragment/screen-coordinate each specific vertex will end up at. +</p> + +<img src="/img/advanced/anti_aliasing_rasterization.png" alt="Image of a triangle being rasterized in OpenGL"/> + +<p> + Here we see a grid of screen pixels where the center of each pixel contains a <def>sample point</def> that is used to determine if a pixel is covered by the triangle. The red sample points are covered by the triangle and a fragment will be generated for that covered pixel. Even though some parts of the triangle edges still enter certain screen pixels, the pixel's sample point is not covered by the inside of the triangle so this pixel won't be influenced by any fragment shader. +</p> + +<p> + You can probably already figure out the origin of aliasing right now. The complete rendered version of the triangle would look like this on your screen: +</p> + +<img src="/img/advanced/anti_aliasing_rasterization_filled.png" alt="Filled triangle as a result of rasterization in OpenGL"/> + +<p> + Due to the limited amount of screen pixels, some pixels will be rendered along an edge and some won't. The result is that we're rendering primitives with non-smooth edges giving rise to the jagged edges we've seen before. +</p> + +<p> + What multisampling does, is not use a single sampling point for determining coverage of the triangle, but multiple sample points (guess where it got its name from). Instead of a single sample point at the center of each pixel we're going to place <code>4</code> <def>subsamples</def> in a general pattern and use those to determine pixel coverage. +</p> + +<img src="/img/advanced/anti_aliasing_sample_points.png" class="clean" alt="Multisampling in OpenGL"/> + +<p> + The left side of the image shows how we would normally determine the coverage of a triangle. This specific pixel won't run a fragment shader (and thus remains blank) since its sample point wasn't covered by the triangle. The right side of the image shows a multisampled version where each pixel contains <code>4</code> sample points. Here we can see that only <code>2</code> of the sample points cover the triangle. +</p> + +<note> + The amount of sample points can be any number we'd like with more samples giving us better coverage precision. +</note> + +<p> + This is where multisampling becomes interesting. We determined that <code>2</code> subsamples were covered by the triangle so the next step is to determine a color for this specific pixel. Our initial guess would be that we run the fragment shader for each covered subsample and later average the colors of each subsample per pixel. In this case we'd run the fragment shader twice on the interpolated vertex data at each subsample and store the resulting color in those sample points. This is (fortunately) <strong>not</strong> how it works, because this would mean we need to run a lot more fragment shaders than without multisampling, drastically reducing performance. +</p> + +<p> + How MSAA really works is that the fragment shader is only run <strong>once</strong> per pixel (for each primitive) regardless of how many subsamples the triangle covers; the fragment shader runs with the vertex data interpolated to the <strong>center</strong> of the pixel. MSAA then uses a larger depth/stencil buffer to determine subsample coverage. The number of subsamples covered determines how much the pixel color contributes to the framebuffer. Because only 2 of the 4 samples were covered in the previous image, half of the triangle's color is mixed with the framebuffer color (in this case the clear color) resulting in a light blue-ish color. +</p> + +<p> + The result is a higher resolution buffer (with higher resolution depth/stencil) where all the primitive edges now produce a smoother pattern. Let's see what multisampling looks like when we determine the coverage of the earlier triangle: +</p> + +<img src="/img/advanced/anti_aliasing_rasterization_samples.png" alt="Rasterization of triangle with multisampling in OpenGL"/> + +<p> + Here each pixel contains 4 subsamples (the irrelevant samples were hidden) where the blue subsamples are covered by the triangle and the gray sample points aren't. Within the inner region of the triangle all pixels will run the fragment shader once where its color output is stored directly in the framebuffer (assuming no blending). At the inner edges of the triangle however not all subsamples will be covered so the result of the fragment shader won't fully contribute to the framebuffer. Based on the number of covered samples, more or less of the triangle fragment's color ends up at that pixel. +</p> + +<p> + For each pixel, the less subsamples are part of the triangle, the less it takes the color of the triangle. If we were to fill in the actual pixel colors we get the following image: +</p> + +<img src="/img/advanced/anti_aliasing_rasterization_samples_filled.png" alt="Rasterized triangle with multisampling in OpenGL"/> + +<p> + The hard edges of the triangle are now surrounded by colors slightly lighter than the actual edge color, which causes the edge to appear smooth when viewed from a distance. +</p> + +<p> + Depth and stencil values are stored per subsample and, even though we only run the fragment shader once, color values are stored per subsample as well for the case of multiple triangles overlapping a single pixel. For depth testing the vertex's depth value is interpolated to each subsample before running the depth test, and for stencil testing we store the stencil values per subsample. This does mean that the size of the buffers are now increased by the amount of subsamples per pixel. +</p> + +<p> + What we've discussed so far is a basic overview of how multisampled anti-aliasing works behind the scenes. The actual logic behind the rasterizer is a bit more complicated, but this brief description should be enough to understand the concept and logic behind multisampled anti-aliasing; enough to delve into the practical aspects. +</p> + +<h2>MSAA in OpenGL</h2> +<p> + If we want to use MSAA in OpenGL we need to use a buffer that is able to store more than one sample value per pixel. We need a new type of buffer that can store a given amount of multisamples and this is called a <def>multisample buffer</def>. +</p> + +<p> + Most windowing systems are able to provide us a multisample buffer instead of a default buffer. GLFW also gives us this functionality and all we need to do is <em>hint</em> GLFW that we'd like to use a multisample buffer with N samples instead of a normal buffer by calling <fun><function id='18'>glfwWindowHint</function></fun> before creating the window: +</p> + +<pre class="cpp"><code> +<function id='18'>glfwWindowHint</function>(GLFW_SAMPLES, 4); +</code></pre> + +<p> + When we now call <fun><function id='20'>glfwCreateWindow</function></fun> we create a rendering window, but this time with a buffer containing 4 subsamples per screen coordinate. This does mean that the size of the buffer is increased by 4. +</p> + +<p> + Now that we asked GLFW for multisampled buffers we need to enable multisampling by calling <fun><function id='60'>glEnable</function></fun> with <var>GL_MULTISAMPLE</var>. On most OpenGL drivers, multisampling is enabled by default so this call is then a bit redundant, but it's usually a good idea to enable it anyways. This way all OpenGL implementations have multisampling enabled. +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_MULTISAMPLE); +</code></pre> + +<p> + Because the actual multisampling algorithms are implemented in the rasterizer in your OpenGL drivers there's not much else we need to do. If we now were to render the green cube from the start of this chapter we should see smoother edges: +</p> + +<img src="/img/advanced/anti_aliasing_multisampled.png" class="clean" alt="Image of a multisampled cube in OpenGL"/> + +<p> + The cube does indeed look a lot smoother and the same will apply for any other object you're drawing in your scene. You can find the source code for this simple example <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/11.1.anti_aliasing_msaa/anti_aliasing_msaa.cpp" target="_blank">here</a>. +</p> + +<h2>Off-screen MSAA</h2> +<p> + Because GLFW takes care of creating the multisampled buffers, enabling MSAA is quite easy. If we want to use our own framebuffers however, we have to generate the multisampled buffers ourselves; now we <strong>do</strong> need to take care of creating multisampled buffers. +</p> + +<p> + There are two ways we can create multisampled buffers to act as attachments for framebuffers: texture attachments and renderbuffer attachments. Quite similar to normal attachments like we've discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> chapter. +</p> + +<h3>Multisampled texture attachments</h3> +<p> + To create a texture that supports storage of multiple sample points we use <fun><function id='101'><function id='52'>glTexImage2D</function>Multisample</function></fun> instead of <fun><function id='52'>glTexImage2D</function></fun> that accepts <var>GL_TEXTURE_2D_MULTISAPLE</var> as its texture target: +</p> + +<pre class="cpp"><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D_MULTISAMPLE, tex); +<function id='101'><function id='52'>glTexImage2D</function>Multisample</function>(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D_MULTISAMPLE, 0); +</code></pre> + +<p> + The second argument sets the number of samples we'd like the texture to have. If the last argument is set to <var>GL_TRUE</var>, the image will use identical sample locations and the same number of subsamples for each texel. +</p> + +<p> + To attach a multisampled texture to a framebuffer we use <fun><function id='81'>glFramebufferTexture2D</function></fun>, but this time with <var>GL_TEXTURE_2D_MULTISAMPLE</var> as the texture type: +</p> + +<pre class="cpp"><code> +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0); +</code></pre> + +<p> + The currently bound framebuffer now has a multisampled color buffer in the form of a texture image. +</p> + +<h3>Multisampled renderbuffer objects</h3> +<p> + Like textures, creating a multisampled renderbuffer object isn't difficult. It is even quite easy since all we need to change is <fun><function id='88'>glRenderbufferStorage</function></fun> to <fun><function id='102'><function id='88'>glRenderbufferStorage</function>Multisample</function></fun> when we configure the (currently bound) renderbuffer's memory storage: +</p> + +<pre class="cpp"><code> +<function id='102'><function id='88'>glRenderbufferStorage</function>Multisample</function>(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height); +</code></pre> + +<p> + The one thing that changed here is the extra second parameter where we set the amount of samples we'd like to use; 4 in this particular case. +</p> + +<h3>Render to multisampled framebuffer</h3> +<p> + Rendering to a multisampled framebuffer is straightforward. Whenever we draw anything while the framebuffer object is bound, the rasterizer will take care of all the multisample operations. However, because a multisampled buffer is a bit special, we can't directly use the buffer for other operations like sampling it in a shader. +</p> + +<p> + A multisampled image contains much more information than a normal image so what we need to do is downscale or <def>resolve</def> the image. Resolving a multisampled framebuffer is generally done through <fun><function id='103'>glBlitFramebuffer</function></fun> that copies a region from one framebuffer to the other while also resolving any multisampled buffers. +</p> + +<p> + <fun><function id='103'>glBlitFramebuffer</function></fun> transfers a given <def>source</def> region defined by 4 screen-space coordinates to a given <def>target</def> region also defined by 4 screen-space coordinates. You may remember from the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> chapter that if we bind to <var>GL_FRAMEBUFFER</var> we're binding to both the read and draw framebuffer targets. We could also bind to those targets individually by binding framebuffers to <var>GL_READ_FRAMEBUFFER</var> and <var>GL_DRAW_FRAMEBUFFER</var> respectively. The <fun><function id='103'>glBlitFramebuffer</function></fun> function reads from those two targets to determine which is the source and which is the target framebuffer. We could then transfer the multisampled framebuffer output to the actual screen by <def>blitting</def> the image to the default framebuffer like so: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_READ_FRAMEBUFFER, multisampledFBO); +<function id='77'>glBindFramebuffer</function>(GL_DRAW_FRAMEBUFFER, 0); +<function id='103'>glBlitFramebuffer</function>(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); +</code></pre> + +<p> + If we then were to render the same application we should get the same output: a lime-green cube displayed with MSAA and again showing significantly less jagged edges: +</p> + +<img src="/img/advanced/anti_aliasing_multisampled.png" class="clean" alt="Image of a multisampled cube in OpenGL"/> + +<p> + You can find the source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/11.2.anti_aliasing_offscreen/anti_aliasing_offscreen.cpp" target="_blank">here</a>. +</p> + +<p> + But what if we wanted to use the texture result of a multisampled framebuffer to do stuff like post-processing? We can't directly use the multisampled texture(s) in the fragment shader. What we can do however is blit the multisampled buffer(s) to a different FBO with a non-multisampled texture attachment. We then use this ordinary color attachment texture for post-processing, effectively post-processing an image rendered via multisampling. This does mean we have to generate a new FBO that acts solely as an intermediate framebuffer object to resolve the multisampled buffer into; a normal 2D texture we can use in the fragment shader. This process looks a bit like this in pseudocode: +</p> + +<pre><code> +unsigned int msFBO = CreateFBOWithMultiSampledAttachments(); +// then create another FBO with a normal texture color attachment +[...] +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); +[...] +while(!<function id='14'>glfwWindowShouldClose</function>(window)) +{ + [...] + + <function id='77'>glBindFramebuffer</function>(msFBO); + ClearFrameBuffer(); + DrawScene(); + // now resolve multisampled buffer(s) into intermediate FBO + <function id='77'>glBindFramebuffer</function>(GL_READ_FRAMEBUFFER, msFBO); + <function id='77'>glBindFramebuffer</function>(GL_DRAW_FRAMEBUFFER, intermediateFBO); + <function id='103'>glBlitFramebuffer</function>(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + // now scene is stored as 2D texture image, so use that image for post-processing + <function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); + ClearFramebuffer(); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, screenTexture); + DrawPostProcessingQuad(); + + [...] +} +</code></pre> + +<p> + If we then implement this into the post-processing code of the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> chapter we're able to create all kinds of cool post-processing effects on a texture of a scene with (almost) no jagged edges. With a grayscale postprocessing filter applied it'll look something like this: +</p> + +<img src="/img/advanced/anti_aliasing_post_processing.png" class="clean" alt="Image of post-processing on a scene drawn with MSAA in OpenGL"/> + +<note> + Because the screen texture is a normal (non-multisampled) texture again, some post-processing filters like <em>edge-detection</em> will introduce jagged edges again. To accommodate for this you could blur the texture afterwards or create your own anti-aliasing algorithm. +</note> + +<p> + You can see that when we want to combine multisampling with off-screen rendering we need to take care of some extra steps. The steps are worth the extra effort though since multisampling significantly boosts the visual quality of your scene. Do note that enabling multisampling can noticeably reduce performance the more samples you use. +</p> + +<h2>Custom Anti-Aliasing algorithm</h2> +<p> + It is possible to directly pass a multisampled texture image to a fragment shader instead of first resolving it. GLSL gives us the option to sample the texture image per subsample so we can create our own custom anti-aliasing algorithms. +</p> + +<p> + To get a texture value per subsample you'd have to define the texture uniform sampler as a <fun>sampler2DMS</fun> instead of the usual <fun>sampler2D</fun>: +</p> + +<pre><code> +uniform sampler2DMS screenTextureMS; +</code></pre> + + <p> + Using the <fun>texelFetch</fun> function it is then possible to retrieve the color value per sample: + </p> + +<pre><code> +vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3); // 4th subsample +</code></pre> + +<p> + We won't go into the details of creating custom anti-aliasing techniques here, but this may be enough to get started on building one yourself. +</p> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Blending.html b/pub/Advanced-OpenGL/Blending.html @@ -0,0 +1,740 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Blending</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Blending</h1> +<p> + <def>Blending</def> in OpenGL is commonly known as the technique to implement <def>transparency</def> within objects. Transparency is all about objects (or parts of them) not having a solid color, but having a combination of colors from the object itself and any other object behind it with varying intensity. A colored glass window is a transparent object; the glass has a color of its own, but the resulting color contains the colors of all the objects behind the glass as well. This is also where the name blending comes from, since we <def>blend</def> several pixel colors (from different objects) to a single color. Transparency thus allows us to see through objects. +</p> + +<img src="/img/advanced/blending_transparency.png" class="clean" alt="Image of full transparent window and partially transparent window"/> + +<p> + Transparent objects can be completely transparent (letting all colors through) or partially transparent (letting colors through, but also some of its own colors). The amount of transparency of an object is defined by its color's <def>alpha</def> value. The alpha color value is the 4th component of a color vector that you've probably seen quite often now. Up until this chapter, we've always kept this 4th component at a value of <code>1.0</code> giving the object <code>0.0</code> transparency. An alpha value of <code>0.0</code> would result in the object having complete transparency. An alpha value of <code>0.5</code> tells us the object's color consist of 50% of its own color and 50% of the colors behind the object. +</p> + +<p> + The textures we've used so far all consisted of <code>3</code> color components: red, green and blue, but some textures also have an embedded alpha channel that contains an <def>alpha</def> value per texel. This alpha value tells us exactly which parts of the texture have transparency and by how much. For example, the following <a href="/img/advanced/blending_transparent_window.png" target="_blank">window texture</a> has an alpha value of <code>0.25</code> at its glass part and an alpha value of <code>0.0</code> at its corners. The glass part would normally be completely red, but since it has 75% transparency it largely shows the page's background through it, making it seem a lot less red: +</p> + +<img src="/img/advanced/blending_transparent_window.png" class="clean" alt="Texture image of window with transparency"/> + +<p> + We'll soon be adding this windowed texture to the scene from the depth testing chapter, but first we'll discuss an easier technique to implement transparency for pixels that are either fully transparent or fully opaque. +</p> + +<h2>Discarding fragments</h2> +<p> + Some effects do not care about partial transparency, but either want to show something or nothing at all based on the color value of a texture. Think of grass; to create something like grass with little effort you generally paste a grass texture onto a 2D quad and place that quad into your scene. However, grass isn't exactly shaped like a 2D square so you only want to display some parts of the grass texture and ignore the others. +</p> + +<p> + The following <a href="/img/textures/grass.png" target="_blank">texture</a> is exactly such a texture where it either is full opaque (an alpha value of <code>1.0</code>) or it is fully transparent (an alpha value of <code>0.0</code>) and nothing in between. You can see that wherever there is no grass, the image shows the page's background color instead of its own. +</p> + +<img src="/img/textures/grass.png" class="clean" style="width:384px; height:384px;" alt="Texture image of grass with transparency"/> + +<p> + So when adding vegetation to a scene we don't want to see a square image of grass, but rather only show the actual grass and see through the rest of the image. We want to <def>discard</def> the fragments that show the transparent parts of the texture, not storing that fragment into the color buffer. +</p> + +<p> + Before we get into that we first need to learn how to load a transparent texture. To load textures with alpha values there's not much we need to change. <code>stb_image</code> automatically loads an image's alpha channel if it's available, but we do need to tell OpenGL our texture now uses an alpha channel in the texture generation procedure: +</p> + +<pre class="cpp"><code> +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); +</code></pre> + +<p> + Also make sure that you retrieve all <code>4</code> color components of the texture in the fragment shader, not just the RGB components: +</p> + +<pre><code> +void main() +{ + // FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0); + FragColor = texture(texture1, TexCoords); +} +</code></pre> + +<p> + Now that we know how to load transparent textures it's time to put it to the test by adding several of these leaves of grass throughout the basic scene introduced in the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter. +</p> + +<p> + We create a small <code>vector</code> array where we add several <code>glm::vec3</code> vectors to represent the location of the grass leaves: +</p> + +<pre><code> +vector&lt;glm::vec3&gt; vegetation; +vegetation.push_back(glm::vec3(-1.5f, 0.0f, -0.48f)); +vegetation.push_back(glm::vec3( 1.5f, 0.0f, 0.51f)); +vegetation.push_back(glm::vec3( 0.0f, 0.0f, 0.7f)); +vegetation.push_back(glm::vec3(-0.3f, 0.0f, -2.3f)); +vegetation.push_back(glm::vec3( 0.5f, 0.0f, -0.6f)); +</code></pre> + +<p> + Each of the grass objects is rendered as a single quad with the grass texture attached to it. It's not a perfect 3D representation of grass, but it's a lot more efficient than loading and rendering a large number of complex models. With a few tricks like adding randomized rotations and scales you can get pretty convincing results with quads. +</p> + +<p> +Because the grass texture is going to be displayed on a quad object we'll need to create another VAO again, fill the VBO, and set the appropriate vertex attribute pointers. Then after we've rendered the floor and the two cubes we're going to render the grass leaves: +</p> + +<pre><code> +<function id='27'>glBindVertexArray</function>(vegetationVAO); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, grassTexture); +for(unsigned int i = 0; i &lt; vegetation.size(); i++) +{ + model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, vegetation[i]); + shader.setMat4("model", model); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +} +</code></pre> + +<p> + Running the application will now look a bit like this: +</p> + +<img src="/img/advanced/blending_no_discard.png" class="clean" alt="Not discarding transparent parts of texture results in weird artifacts in OpenGL"/> + +<p> + This happens because OpenGL by default does not know what to do with alpha values, nor when to discard them. We have to manually do this ourselves. Luckily this is quite easy thanks to the use of shaders. GLSL gives us the <code>discard</code> command that (once called) ensures the fragment will not be further processed and thus not end up into the color buffer. Thanks to this command we can check whether a fragment has an alpha value below a certain threshold and if so, discard the fragment as if it had never been processed: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D texture1; + +void main() +{ + vec4 texColor = texture(texture1, TexCoords); + if(texColor.a &lt; 0.1) + discard; + FragColor = texColor; +} +</code></pre> + + +<p> + Here we check if the sampled texture color contains an alpha value lower than a threshold of <code>0.1</code> and if so, discard the fragment. This fragment shader ensures us that it only renders fragments that are not (almost) completely transparent. Now it'll look like it should: +</p> + + +<img src="/img/advanced/blending_discard.png" class="clean" alt="Image of grass leaves rendered with fragment discarding in OpenGL"/> + +<note> + Note that when sampling textures at their borders, OpenGL interpolates the border values with the next repeated value of the texture (because we set its wrapping parameters to <var>GL_REPEAT</var> by default). This is usually okay, but since we're using transparent values, the top of the texture image gets its transparent value interpolated with the bottom border's solid color value. The result is then a slightly semi-transparent colored border you may see wrapped around your textured quad. To prevent this, set the texture wrapping method to <var>GL_CLAMP_TO_EDGE</var> whenever you use alpha textures that you don't want to repeat: + +<pre><code> +<function id='15'>glTexParameter</function>i( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +</code></pre> +</note> + +<p> + You can find the source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/3.1.blending_discard/blending_discard.cpp" target="_blank">here</a>. +</p> + + +<h2>Blending</h2> +<p> + While discarding fragments is great and all, it doesn't give us the flexibility to render semi-transparent images; we either render the fragment or completely discard it. To render images with different levels of transparency we have to enable <def>blending</def>. Like most of OpenGL's functionality we can enable blending by enabling <var>GL_BLEND</var>: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_BLEND); +</code></pre> + +<p> + Now that we've enabled blending we need to tell OpenGL <strong>how</strong> it should actually blend. +</p> + +<p> + Blending in OpenGL happens with the following equation: +</p> + +\begin{equation}\bar{C}_{result} = \bar{\color{green}C}_{source} * \color{green}F_{source} + \bar{\color{red}C}_{destination} * \color{red}F_{destination}\end{equation} + +<ul> + <li>\(\bar{\color{green}C}_{source}\): the source color vector. This is the color output of the fragment shader.</li> + <li>\(\bar{\color{red}C}_{destination}\): the destination color vector. This is the color vector that is currently stored in the color buffer.</li> + <li>\(\color{green}F_{source}\): the source factor value. Sets the impact of the alpha value on the source color.</li> + <li>\(\color{red}F_{destination}\): the destination factor value. Sets the impact of the alpha value on the destination color.</li> +</ul> + +<p> + After the fragment shader has run and all the tests have passed, this <def>blend equation</def> is let loose on the fragment's color output and with whatever is currently in the color buffer. The source and destination colors will automatically be set by OpenGL, but the source and destination factor can be set to a value of our choosing. Let's start with a simple example: +</p> + +<img src="/img/advanced/blending_equation.png" class="clean" alt="Two squares where one has alpha value lower than 1"/> + +<p> + We have two squares where we want to draw the semi-transparent green square on top of the red square. The red square will be the destination color (and thus should be first in the color buffer) and we are now going to draw the green square over the red square. +</p> + +<p> + The question then arises: what do we set the factor values to? Well, we at least want to multiply the green square with its alpha value so we want to set the \(F_{src}\) equal to the alpha value of the source color vector which is <code>0.6</code>. Then it makes sense to let the destination square have a contribution equal to the remainder of the alpha value. If the green square contributes 60% to the final color we want the red square to contribute 40% of the final color e.g. <code>1.0 - 0.6</code>. So we set \(F_{destination}\) equal to one minus the alpha value of the source color vector. The equation thus becomes: +</p> + +\begin{equation}\bar{C}_{result} = \begin{pmatrix} \color{red}{0.0} \\ \color{green}{1.0} \\ \color{blue}{0.0} \\ \color{purple}{0.6} \end{pmatrix} * \color{green}{0.6} + \begin{pmatrix} \color{red}{1.0} \\ \color{green}{0.0} \\ \color{blue}{0.0} \\ \color{purple}{1.0} \end{pmatrix} * (\color{red}{1 - 0.6}) \end{equation} + +<p> + The result is that the combined square fragments contain a color that is 60% green and 40% red: +</p> + +<img src="/img/advanced/blending_equation_mixed.png" class="clean" alt="Two containers where one has alpha value lower than 1"/> + +<p> + The resulting color is then stored in the color buffer, replacing the previous color. +</p> + +<p> + So this is great and all, but how do we actually tell OpenGL to use factors like that? Well it just so happens that there is a function for this called <fun><function id='70'>glBlendFunc</function></fun>. +</p> + +<p> + The <fun><function id='70'>glBlendFunc</function>(GLenum sfactor, GLenum dfactor)</fun> function expects two parameters that set the option for the <def>source</def> and <def>destination factor</def>. OpenGL defined quite a few options for us to set of which we'll list the most common options below. Note that the constant color vector \(\bar{\color{blue}C}_{constant}\) can be separately set via the <fun><function id='73'>glBlendColor</function></fun> function. +</p> + +<table> + <tr> + <th>Option</th> + <th>Value</th> + </tr> + <tr> + <td><code>GL_ZERO</code></td> + <td>Factor is equal to \(0\).</td> + </tr> + <tr> + <td><code>GL_ONE</code></td> + <td>Factor is equal to \(1\).</td> + </tr> + <tr> + <td><code>GL_SRC_COLOR</code></td> + <td>Factor is equal to the source color vector \(\bar{\color{green}C}_{source}\).</td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_SRC_COLOR</code></td> + <td>Factor is equal to \(1\) minus the source color vector: \(1 - \bar{\color{green}C}_{source}\). </td> + </tr><tr> + <td><code>GL_DST_COLOR</code></td> + <td>Factor is equal to the destination color vector \(\bar{\color{red}C}_{destination}\)</td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_DST_COLOR</code></td> + <td>Factor is equal to \(1\) minus the destination color vector: \(1 - \bar{\color{red}C}_{destination}\).</td> + </tr> + <tr> + <td><code>GL_SRC_ALPHA</code></td> + <td>Factor is equal to the \(alpha\) component of the source color vector \(\bar{\color{green}C}_{source}\). </td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_SRC_ALPHA</code></td> + <td>Factor is equal to \(1 - alpha\) of the source color vector \(\bar{\color{green}C}_{source}\).</td> + </tr> + <tr> + <td><code>GL_DST_ALPHA</code></td> + <td>Factor is equal to the \(alpha\) component of the destination color vector \(\bar{\color{red}C}_{destination}\). </td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_DST_ALPHA</code></td> + <td>Factor is equal to \(1 - alpha\) of the destination color vector \(\bar{\color{red}C}_{destination}\).</td> + </tr> + <tr> + <td><code>GL_CONSTANT_COLOR</code></td> + <td>Factor is equal to the constant color vector \(\bar{\color{blue}C}_{constant}\). </td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_CONSTANT_COLOR</code></td> + <td>Factor is equal to \(1\) - the constant color vector \(\bar{\color{blue}C}_{constant}\).</td> + </tr> + <tr> + <td><code>GL_CONSTANT_ALPHA</code></td> + <td>Factor is equal to the \(alpha\) component of the constant color vector \(\bar{\color{blue}C}_{constant}\). </td> + </tr> + <tr> + <td><code>GL_ONE_MINUS_CONSTANT_ALPHA</code></td> + <td>Factor is equal to \(1 - alpha\) of the constant color vector \(\bar{\color{blue}C}_{constant}\).</td> + </tr> +</table> + +<p> + To get the blending result of our little two square example, we want to take the \(alpha\) of the source color vector for the source factor and \(1 - alpha\) of the same color vector for the destination factor. This translates to <fun><function id='70'>glBlendFunc</function></fun> as follows: +</p> + +<pre><code> +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +</code></pre> + +<p> + It is also possible to set different options for the RGB and alpha channel individually using <fun><function id='71'><function id='70'>glBlendFunc</function>Separate</function></fun>: +</p> + +<pre><code> +<function id='71'><function id='70'>glBlendFunc</function>Separate</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); +</code></pre> + +<p> + This function sets the RGB components as we've set them previously, but only lets the resulting alpha component be influenced by the source's alpha value. +</p> + +<p> + OpenGL gives us even more flexibility by allowing us to change the operator between the source and destination part of the equation. Right now, the source and destination components are added together, but we could also subtract them if we want. <fun><function id='72'>glBlendEquation</function>(GLenum mode)</fun> allows us to set this operation and has 5 possible options: +</p> + +<ul> + <li><code>GL_FUNC_ADD</code>: the default, adds both colors to each other: \(\bar{C}_{result} = \color{green}{Src} + \color{red}{Dst}\).</li> + <li><code>GL_FUNC_SUBTRACT</code>: subtracts both colors from each other: \(\bar{C}_{result} = \color{green}{Src} - \color{red}{Dst}\).</li> + <li><code>GL_FUNC_REVERSE_SUBTRACT</code>: subtracts both colors, but reverses order: \(\bar{C}_{result} = \color{red}{Dst} - \color{green}{Src}\).</li> + <li><code>GL_MIN</code>: takes the component-wise minimum of both colors: \(\bar{C}_{result} = min(\color{red}{Dst}, \color{green}{Src})\).</li> + <li><code>GL_MAX</code>: takes the component-wise maximum of both colors: \(\bar{C}_{result} = max(\color{red}{Dst}, \color{green}{Src})\).</li> +</ul> + +<p> + Usually we can simply omit a call to <fun><function id='72'>glBlendEquation</function></fun> because <var>GL_FUNC_ADD</var> is the preferred blending equation for most operations, but if you're really trying your best to break the mainstream circuit any of the other equations could suit your needs. +</p> + +<h2>Rendering semi-transparent textures</h2> +<p> + Now that we know how OpenGL works with regards to blending it's time to put our knowledge to the test by adding several semi-transparent windows. We'll be using the same scene as in the start of this chapter, but instead of rendering a grass texture we're now going to use the <a href="/img/advanced/blending_transparent_window.png" target="_blank">transparent window</a> texture from the start of this chapter. +</p> + +<p> + First, during initialization we enable blending and set the appropriate blending function: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_BLEND); +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +</code></pre> + +<p> + Since we enabled blending there is no need to discard fragments so we'll reset the fragment shader to its original version: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D texture1; + +void main() +{ + FragColor = texture(texture1, TexCoords); +} +</code></pre> + +<p> + This time (whenever OpenGL renders a fragment) it combines the current fragment's color with the fragment color currently in the color buffer based on the alpha value of <var>FragColor</var>. Since the glass part of the window texture is semi-transparent we should be able to see the rest of the scene by looking through this window. +</p> + + +<img src="/img/advanced/blending_incorrect_order.png" class="clean" alt="A blended scene in OpenGL where order is incorrect."/> + +<p> + If you take a closer look however, you may notice something is off. The transparent parts of the front window are occluding the windows in the background. Why is this happening? +</p> + +<p> + The reason for this is that depth testing works a bit tricky combined with blending. When writing to the depth buffer, the depth test does not care if the fragment has transparency or not, so the transparent parts are written to the depth buffer as any other value. The result is that the background windows are tested on depth as any other opaque object would be, ignoring transparency. Even though the transparent part should show the windows behind it, the depth test discards them. +</p> + +<p> + So we cannot simply render the windows however we want and expect the depth buffer to solve all our issues for us; this is also where blending gets a little nasty. To make sure the windows show the windows behind them, we have to draw the windows in the background first. This means we have to manually sort the windows from furthest to nearest and draw them accordingly ourselves. +</p> + +<note> + Note that with fully transparent objects like the grass leaves we have the option to discard the transparent fragments instead of blending them, saving us a few of these headaches (no depth issues). +</note> + +<h2>Don't break the order</h2> +<p> + To make blending work for multiple objects we have to draw the most distant object first and the closest object last. The normal non-blended objects can still be drawn as normal using the depth buffer so they don't have to be sorted. We do have to make sure they are drawn first before drawing the (sorted) transparent objects. When drawing a scene with non-transparent and transparent objects the general outline is usually as follows: +</p> + +<ol> + <li>Draw all opaque objects first.</li> + <li>Sort all the transparent objects.</li> + <li>Draw all the transparent objects in sorted order.</li> +</ol> + +<p> + One way of sorting the transparent objects is to retrieve the distance of an object from the viewer's perspective. This can be achieved by taking the distance between the camera's position vector and the object's position vector. We then store this distance together with the corresponding position vector in a <fun>map</fun> data structure from the STL library. A <fun>map</fun> automatically sorts its values based on its keys, so once we've added all positions with their distance as the key they're automatically sorted on their distance value: +</p> + +<pre><code> +std::map&lt;float, glm::vec3&gt; sorted; +for (unsigned int i = 0; i &lt; windows.size(); i++) +{ + float distance = glm::length(camera.Position - windows[i]); + sorted[distance] = windows[i]; +} +</code></pre> + +<p> + The result is a sorted container object that stores each of the window positions based on their <var>distance</var> key value from lowest to highest distance. +</p> + +<p> + Then, this time when rendering, we take each of the map's values in reverse order (from farthest to nearest) and then draw the corresponding windows in correct order: +</p> + +<pre><code> +for(std::map&lt;float,glm::vec3&gt;::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) +{ + model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, it-&gt;second); + shader.setMat4("model", model); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +} +</code></pre> + +<p> + We take a reverse iterator from the <fun>map</fun> to iterate through each of the items in reverse order and then translate each window quad to the corresponding window position. This relatively simple approach to sorting transparent objects fixes the previous problem and now the scene looks like this: +</p> + +<img src="/img/advanced/blending_sorted.png" class="clean" alt="Image of an OpenGL scene with blending enabled, objects are sorted from far to near"/> + +<p> + You can find the complete source code with sorting <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/3.2.blending_sort/blending_sorted.cpp" target="_blank">here</a>. +</p> + +<p> + While this approach of sorting the objects by their distance works well for this specific scenario, it doesn't take rotations, scaling or any other transformation into account and weirdly shaped objects need a different metric than simply a position vector. +</p> + +<p> + Sorting objects in your scene is a difficult feat that depends greatly on the type of scene you have, let alone the extra processing power it costs. Completely rendering a scene with solid and transparent objects isn't all that easy. There are more advanced techniques like <def>order independent transparency</def> but these are out of the scope of this chapter. For now you'll have to live with normally blending your objects, but if you're careful and know the limitations you can get pretty decent blending implementations. +</p> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Cubemaps.html b/pub/Advanced-OpenGL/Cubemaps.html @@ -0,0 +1,878 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Cubemaps</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Cubemaps</h1> +<p> + We've been using 2D textures for a while now, but there are more texture types we haven't explored yet and in this chapter we'll discuss a texture type that is a combination of multiple textures mapped into one: a <def>cube map</def>. +</p> + +<p> + A cubemap is a texture that contains 6 individual 2D textures that each form one side of a cube: a textured cube. You may be wondering what the point is of such a cube? Why bother combining 6 individual textures into a single entity instead of just using 6 individual textures? Well, cube maps have the useful property that they can be indexed/sampled using a direction vector. Imagine we have a 1x1x1 unit cube with the origin of a direction vector residing at its center. Sampling a texture value from the cube map with an orange direction vector looks a bit like this: +</p> + +<img src="/img/advanced/cubemaps_sampling.png" class="clean" alt="Indexing/Sampling from a cubemap in OpenGL"/> + +<note> + The magnitude of the direction vector doesn't matter. As long as a direction is supplied, OpenGL retrieves the corresponding texels that the direction hits (eventually) and returns the properly sampled texture value. +</note> + +<p> + If we imagine we have a cube shape that we attach such a cubemap to, this direction vector would be similar to the (interpolated) local vertex position of the cube. This way we can sample the cubemap using the cube's actual position vectors as long as the cube is centered on the origin. We thus consider all vertex positions of the cube to be its texture coordinates when sampling a cubemap. The result is a texture coordinate that accesses the proper individual <def>face</def> texture of the cubemap. +</p> + +<h2>Creating a cubemap</h2> +<p> + A cubemap is a texture like any other texture, so to create one we generate a texture and bind it to the proper texture target before we do any further texture operations. This time binding it to <var>GL_TEXTURE_CUBE_MAP</var>: +</p> + +<pre class="cpp"><code> +unsigned int textureID; +<function id='50'>glGenTextures</function>(1, &textureID); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, textureID); +</code></pre> + +<p> + Because a cubemap contains 6 textures, one for each face, we have to call <fun><function id='52'>glTexImage2D</function></fun> six times with their parameters set similarly to the previous chapters. This time however, we have to set the texture <em>target</em> parameter to match a specific face of the cubemap, telling OpenGL which side of the cubemap we're creating a texture for. This means we have to call <fun><function id='52'>glTexImage2D</function></fun> once for each face of the cubemap. +</p> + +<p> + Since we have 6 faces OpenGL gives us 6 special texture targets for targeting a face of the cubemap: +</p> + +<table> + <tr> + <th>Texture target</th> + <th>Orientation</th> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_POSITIVE_X</code></td> + <td>Right</td> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_NEGATIVE_X</code></td> + <td>Left</td> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_POSITIVE_Y</code></td> + <td>Top</td> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_NEGATIVE_Y</code></td> + <td>Bottom</td> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_POSITIVE_Z</code></td> + <td>Back</td> + </tr> + <tr> + <td><code>GL_TEXTURE_CUBE_MAP_NEGATIVE_Z</code></td> + <td>Front</td> + </tr> +</table> + +<p> + Like many of OpenGL's enums, their behind-the-scenes <fun>int</fun> value is linearly incremented, so if we were to have an array or vector of texture locations we could loop over them by starting with <var>GL_TEXTURE_CUBE_MAP_POSITIVE_X</var> and incrementing the enum by 1 each iteration, effectively looping through all the texture targets: +</p> + +<pre><code> +int width, height, nrChannels; +unsigned char *data; +for(unsigned int i = 0; i &lt; textures_faces.size(); i++) +{ + data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0); + <function id='52'>glTexImage2D</function>( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, + 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data + ); +} +</code></pre> + +<p> + Here we have a <fun>vector</fun> called <var>textures_faces</var> that contain the locations of all the textures required for the cubemap in the order as given in the table. This generates a texture for each face of the currently bound cubemap. +</p> + +<p> + Because a cubemap is a texture like any other texture, we will also specify its wrapping and filtering methods: +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +</code></pre> + +<p> + Don't be scared by the <var>GL_TEXTURE_WRAP_R</var>, this simply sets the wrapping method for the texture's <code>R</code> coordinate which corresponds to the texture's 3rd dimension (like <code>z</code> for positions). We set the wrapping method to <var>GL_CLAMP_TO_EDGE</var> since texture coordinates that are exactly between two faces may not hit an exact face (due to some hardware limitations) so by using <var>GL_CLAMP_TO_EDGE</var> OpenGL always returns their edge values whenever we sample between faces. +</p> + +<p> + Then before drawing the objects that will use the cubemap, we activate the corresponding texture unit and bind the cubemap before rendering; not much of a difference compared to normal 2D textures. +</p> + +<p> + Within the fragment shader we also have to use a different sampler of the type <code>samplerCube</code> that we sample from using the <fun>texture</fun> function, but this time using a <code>vec3</code> direction vector instead of a <code>vec2</code>. An example of fragment shader using a cubemap looks like this: +</p> + +<pre><code> +in vec3 textureDir; // direction vector representing a 3D texture coordinate +uniform samplerCube cubemap; // cubemap texture sampler + +void main() +{ + FragColor = texture(cubemap, textureDir); +} +</code></pre> + + +<p> + That is still great and all, but why bother? Well, it just so happens that there are quite a few interesting techniques that are a lot easier to implement with a cubemap. One of those techniques is creating a <def>skybox</def>. +</p> + +<h1>Skybox</h1> +<p> + A skybox is a (large) cube that encompasses the entire scene and contains 6 images of a surrounding environment, giving the player the illusion that the environment he's in is actually much larger than it actually is. Some examples of skyboxes used in videogames are images of mountains, of clouds, or of a starry night sky. An example of a skybox, using starry night sky images, can be seen in the following screenshot of the third elder scrolls game: +</p> + +<img src="/img/advanced/cubemaps_morrowind.jpg" alt="Image of morrowind with a skybox"/> + +<p> + You probably guessed by now that skyboxes like this suit cubemaps perfectly: we have a cube that has 6 faces and needs to be textured per face. In the previous image they used several images of a night sky to give the illusion the player is in some large universe while he's actually inside a tiny little box. +</p> + +<p> + There are usually enough resources online where you could find skyboxes like that. These skybox images usually have the following pattern: +</p> + +<img src="/img/advanced/cubemaps_skybox.png" class="clean" alt="Image of a skybox for a cubemap in OpenGL"/> + +<p> + If you would fold those 6 sides into a cube you'd get the completely textured cube that simulates a large landscape. Some resources provide the skybox in a format like that in which case you'd have to manually extract the 6 face images, but in most cases they're provided as 6 single texture images. +</p> + +<p> + This particular (high-quality) skybox is what we'll use for our scene and can be downloaded <a href="/img/textures/skybox.zip" target="_blank">here</a>. +</p> + +<h2>Loading a skybox</h2> +<p> + Since a skybox is by itself just a cubemap, loading a skybox isn't too different from what we've seen at the start of this chapter. To load the skybox we're going to use the following function that accepts a <fun>vector</fun> of 6 texture locations: +</p> + +<pre><code> +unsigned int loadCubemap(vector&lt;std::string&gt; faces) +{ + unsigned int textureID; + <function id='50'>glGenTextures</function>(1, &textureID); + <function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, textureID); + + int width, height, nrChannels; + for (unsigned int i = 0; i &lt; faces.size(); i++) + { + unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); + if (data) + { + <function id='52'>glTexImage2D</function>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, + 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data + ); + stbi_image_free(data); + } + else + { + std::cout &lt;&lt; "Cubemap tex failed to load at path: " &lt;&lt; faces[i] &lt;&lt; std::endl; + stbi_image_free(data); + } + } + <function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + return textureID; +} +</code></pre> + +<p> + The function itself shouldn't be too surprising. It is basically all the cubemap code we've seen in the previous section, but combined in a single manageable function. +</p> + +<p> + Now, before we call this function we'll load the appropriate texture paths in a vector in the order as specified by the cubemap enums: +</p> + +<pre><code> +vector&lt;std::string&gt; faces; +{ + "right.jpg", + "left.jpg", + "top.jpg", + "bottom.jpg", + "front.jpg", + "back.jpg" +}; +unsigned int cubemapTexture = loadCubemap(faces); +</code></pre> + +<p> + We loaded the skybox as a cubemap with <var>cubemapTexture</var> as its id. We can now finally bind it to a cube to replace that lame clear color we've been using all this time. +</p> + +<h2>Displaying a skybox</h2> +<p> + Because a skybox is drawn on a cube we'll need another VAO, VBO and a fresh set of vertices like any other 3D object. You can get its vertex data <a href="/code_viewer.php?code=advanced/cubemaps_skybox_data" target="_blank">here</a>. +</p> + +<p> + A cubemap used to texture a 3D cube can be sampled using the local positions of the cube as its texture coordinates. When a cube is centered on the origin (0,0,0) each of its position vectors is also a direction vector from the origin. This direction vector is exactly what we need to get the corresponding texture value at that specific cube's position. For this reason we only need to supply position vectors and don't need texture coordinates. +</p> + +<p> + To render the skybox we'll need a new set of shaders which aren't too complicated. Because we only have one vertex attribute the vertex shader is quite simple: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +out vec3 TexCoords; + +uniform mat4 projection; +uniform mat4 view; + +void main() +{ + TexCoords = aPos; + gl_Position = projection * view * vec4(aPos, 1.0); +} +</code></pre> + +<p> + The interesting part of this vertex shader is that we set the incoming local position vector as the outcoming texture coordinate for (interpolated) use in the fragment shader. The fragment shader then takes these as input to sample a <code>samplerCube</code>: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec3 TexCoords; + +uniform samplerCube skybox; + +void main() +{ + FragColor = texture(skybox, TexCoords); +} +</code></pre> + +<p> + The fragment shader is relatively straightforward. We take the vertex attribute's interpolated position vector as the texture's direction vector and use it to sample the texture values from the cubemap. +</p> + +<p> + Rendering the skybox is easy now that we have a cubemap texture, we simply bind the cubemap texture and the <var>skybox</var> sampler is automatically filled with the skybox cubemap. To draw the skybox we're going to draw it as the first object in the scene and disable depth writing. This way the skybox will always be drawn at the background of all the other objects since the unit cube is most likely smaller than the rest of the scene. +</p> + +<pre><code> +<function id='65'>glDepthMask</function>(GL_FALSE); +skyboxShader.use(); +// ... set view and projection matrix +<function id='27'>glBindVertexArray</function>(skyboxVAO); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, cubemapTexture); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +<function id='65'>glDepthMask</function>(GL_TRUE); +// ... draw rest of the scene +</code></pre> + +<p> + If you run this you will get into difficulties though. We want the skybox to be centered around the player so that no matter how far the player moves, the skybox won't get any closer, giving the impression the surrounding environment is extremely large. The current view matrix however transforms all the skybox's positions by rotating, scaling and translating them, so if the player moves, the cubemap moves as well! We want to remove the translation part of the view matrix so only rotation will affect the skybox's position vectors. +</p> + +<p> + You may remember from the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">basic lighting</a> chapter that we can remove the translation section of transformation matrices by taking the upper-left 3x3 matrix of the 4x4 matrix. We can achieve this by converting the view matrix to a 3x3 matrix (removing translation) and converting it back to a 4x4 matrix: +</p> + +<pre><code> +glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix())); +</code></pre> + +<p> + This removes any translation, but keeps all rotation transformations so the user can still look around the scene. +</p> + +<p> + The result is a scene that instantly looks enormous due to our skybox. If you'd fly around the basic container you immediately get a sense of scale which dramatically improves the realism of the scene. The result looks something like this: +</p> + +<img src="/img/advanced/cubemaps_skybox_result.png" class="clean" alt="Image of a skybox in an OpenGL scene"/> + +<p> + Try experimenting with different skyboxes and see how they can have an enormous impact on the look and feel of your scene. +</p> + +<h2>An optimization</h2> +<p> + Right now we've rendered the skybox first before we rendered all the other objects in the scene. This works great, but isn't too efficient. If we render the skybox first we're running the fragment shader for each pixel on the screen even though only a small part of the skybox will eventually be visible; fragments that could have easily been discarded using <def>early depth testing</def> saving us valuable bandwidth. +</p> + +<p> + So to give us a slight performance boost we're going to render the skybox last. This way, the depth buffer is completely filled with all the scene's depth values so we only have to render the skybox's fragments wherever the early depth test passes, greatly reducing the number of fragment shader calls. The problem is that the skybox will most likely render on top of all other objects since it's only a 1x1x1 cube, succeeding most depth tests. Simply rendering it without depth testing is not a solution since the skybox will then still overwrite all the other objects in the scene as it's rendered last. We need to trick the depth buffer into believing that the skybox has the maximum depth value of <code>1.0</code> so that it fails the depth test wherever there's a different object in front of it. +</p> + +<p> + In the <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">coordinate systems</a> chapter we said that <em>perspective division</em> is performed after the vertex shader has run, dividing the <var>gl_Position</var>'s <code>xyz</code> coordinates by its <code>w</code> component. We also know from the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter that the <code>z</code> component of the resulting division is equal to that vertex's depth value. Using this information we can set the <code>z</code> component of the output position equal to its <code>w</code> component which will result in a <code>z</code> component that is always equal to <code>1.0</code>, because when the perspective division is applied its <code>z</code> component translates to <code>w</code> / <code>w</code> = <code>1.0</code>: +</p> + +<pre><code> +void main() +{ + TexCoords = aPos; + vec4 pos = projection * view * vec4(aPos, 1.0); + gl_Position = pos.xyww; +} +</code></pre> + +<p> + The resulting <em>normalized device coordinates</em> will then always have a <code>z</code> value equal to <code>1.0</code>: the maximum depth value. The skybox will as a result only be rendered wherever there are no objects visible (only then it will pass the depth test, everything else is in front of the skybox). +</p> + +<p> + We do have to change the depth function a little by setting it to <var>GL_LEQUAL</var> instead of the default <var>GL_LESS</var>. The depth buffer will be filled with values of <code>1.0</code> for the skybox, so we need to make sure the skybox passes the depth tests with values <em>less than or equal</em> to the depth buffer instead of <em>less than</em>. +</p> + +<p> + You can find the more optimized version of the source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/6.1.cubemaps_skybox/cubemaps_skybox.cpp" target="_blank">here</a>. +</p> + +<h1>Environment mapping</h1> +<p> + We now have the entire surrounding environment mapped in a single texture object and we could use that information for more than just a skybox. Using a cubemap with an environment, we could give objects reflective or refractive properties. Techniques that use an environment cubemap like this are called <def>environment mapping</def> techniques and the two most popular ones are <def>reflection</def> and <def>refraction</def>. +</p> + +<h2>Reflection</h2> +<p> + Reflection is the property that an object (or part of an object) <def>reflects</def> its surrounding environment e.g. the object's colors are more or less equal to its environment based on the angle of the viewer. A mirror for example is a reflective object: it reflects its surroundings based on the viewer's angle. +</p> + +<p> + The basics of reflection are not that difficult. The following image shows how we can calculate a <def>reflection vector</def> and use that vector to sample from a cubemap: +</p> + +<img src="/img/advanced/cubemaps_reflection_theory.png" class="clean" alt="Image of how to calculate reflection."/> + +<p> + We calculate a reflection vector \(\color{green}{\bar{R}}\) around the object's normal vector \(\color{red}{\bar{N}}\) based on the view direction vector \(\color{gray}{\bar{I}}\). We can calculate this reflection vector using GLSL's built-in <fun>reflect</fun> function. The resulting vector \(\color{green}{\bar{R}}\) is then used as a direction vector to index/sample the cubemap, returning a color value of the environment. The resulting effect is that the object seems to reflect the skybox. +</p> + +<p> + Since we already have a skybox setup in our scene, creating reflections isn't too difficult. We'll change the fragment shader used by the container to give the container reflective properties: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec3 Normal; +in vec3 Position; + +uniform vec3 cameraPos; +uniform samplerCube skybox; + +void main() +{ + vec3 I = normalize(Position - cameraPos); + vec3 R = reflect(I, normalize(Normal)); + FragColor = vec4(texture(skybox, R).rgb, 1.0); +} +</code></pre> + +<p> + We first calculate the view/camera direction vector <var>I</var> and use this to calculate the reflect vector <var>R</var> which we then use to sample from the skybox cubemap. Note that we have the fragment's interpolated <var>Normal</var> and <var>Position</var> variable again so we'll need to adjust the vertex shader as well: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +out vec3 Normal; +out vec3 Position; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + Normal = mat3(transpose(inverse(model))) * aNormal; + Position = vec3(model * vec4(aPos, 1.0)); + gl_Position = projection * view * vec4(Position, 1.0); +} +</code></pre> + +<p> + We're using normal vectors so we'll want to transform them with a normal matrix again. The <var>Position</var> output vector is a world-space position vector. This <var>Position</var> output of the vertex shader is used to calculate the view direction vector in the fragment shader. +</p> + +<p> + Because we're using normals you'll want to update the <a href="/code_viewer.php?code=lighting/basic_lighting_vertex_data" target="_blank">vertex data</a> and update the attribute pointers as well. Also make sure to set the <var>cameraPos</var> uniform. +</p> + +<p> + Then we also want to bind the cubemap texture before rendering the container: +</p> + +<pre class="cpp"><code> +<function id='27'>glBindVertexArray</function>(cubeVAO); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, skyboxTexture); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +</code></pre> + +<p> + Compiling and running your code gives you a container that acts like a perfect mirror. The surrounding skybox is perfectly reflected on the container: +</p> + +<img src="/img/advanced/cubemaps_reflection.png" class="clean" alt="Image of a cube reflecting a skybox via cubemaps via environment mapping."/> + +<p> + You can find the full source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/6.2.cubemaps_environment_mapping/cubemaps_environment_mapping.cpp" target="_blank">here</a>. +</p> + +<p> + When reflection is applied to an entire object (like the container) the object looks as if it has a high reflective material like steel or chrome. If we were to load a more interesting object (like the backpack model from the <a href="https://learnopengl.com/Model-Loading/Model" target="_blank">model loading</a> chapters) we'd get the effect that the object looks to be entirely made out of chrome: +</p> + +<img src="/img/advanced/cubemaps_reflection_nanosuit.png" alt="Image of a Backpack model reflecting a skybox via cubemaps via environment mapping."/> + +<p> + This looks quite awesome, but in reality most models aren't all completely reflective. We could for instance introduce <def>reflection maps</def> that give the models another extra level of detail. Just like diffuse and specular maps, reflection maps are texture images that we can sample to determine the reflectivity of a fragment. Using these reflection maps we can determine which parts of the model show reflection and by what intensity. <!--In the exercise of this chapter it's up to you to introduce reflection maps in the model loader we created earlier, significantly boosting the detail of the 3D object.--> +</p> + +<h2>Refraction</h2> +<p> + Another form of environment mapping is called <def>refraction</def> and is similar to reflection. Refraction is the change in direction of light due to the change of the material the light flows through. Refraction is what we commonly see with water-like surfaces where the light doesn't enter straight through, but bends a little. It's like looking at your arm when it's halfway in the water. +</p> + +<p> + Refraction is described by <a href="http://en.wikipedia.org/wiki/Snell%27s_law" target="_blank">Snell's law</a> that with environment maps looks a bit like this: +</p> + +<img src="/img/advanced/cubemaps_refraction_theory.png" class="clean" alt="Image explaining refraction of light for use with cubemaps."/> + +<p> + Again, we have a view vector \(\color{gray}{\bar{I}}\), a normal vector \(\color{red}{\bar{N}}\) and this time a resulting refraction vector \(\color{green}{\bar{R}}\). As you can see, the direction of the view vector is slightly bend. This resulting bended vector \(\color{green}{\bar{R}}\) is then used to sample from the cubemap. +</p> + +<p> + Refraction is fairly easy to implement using GLSL's built-in <fun>refract</fun> function that expects a normal vector, a view direction, and a ratio between both materials' <def>refractive indices</def>. +</p> + +<p> + The refractive index determines the amount light distorts/bends in a material where each material has its own refractive index. A list of the most common refractive indices are given in the following table: +</p> + +<table> + <tr> + <th>Material</th> + <th>Refractive index</th> + </tr> + <tr> + <td>Air</td> + <td>1.00</td> + </tr> + <tr> + <td>Water</td> + <td>1.33</td> + </tr> + <tr> + <td>Ice</td> + <td>1.309</td> + </tr> + <tr> + <td>Glass</td> + <td>1.52</td> + </tr> + <tr> + <td>Diamond</td> + <td>2.42</td> + </tr> +</table> + +<p> + We use these refractive indices to calculate the ratio between both materials the light passes through. In our case, the light/view ray goes from <em>air</em> to <em>glass</em> (if we assume the object is made of glass) so the ratio becomes \(\frac{1.00}{1.52} = 0.658\). +</p> + +<p> + We already have the cubemap bound, supplied the vertex data with normals, and set the camera position as a uniform. The only thing we have to change is the fragment shader: +</p> + +<pre><code> +void main() +{ + float ratio = 1.00 / 1.52; + vec3 I = normalize(Position - cameraPos); + vec3 R = refract(I, normalize(Normal), ratio); + FragColor = vec4(texture(skybox, R).rgb, 1.0); +} +</code></pre> + +<p> + By changing the refractive indices you can create completely different visual results. Compiling the application and running the results on the container object is not so interesting though as it doesn't really show the effect refraction has aside that it acts as a magnifying glass right now. Using the same shaders on the loaded 3D model however does show us the effect we're looking for: a glass-like object. +</p> + +<img src="/img/advanced/cubemaps_refraction.png" alt="Image of environment maps using refraction in OpenGL"/> + +<p> + You can imagine that with the right combination of lighting, reflection, refraction and vertex movement, you can create pretty neat water graphics. Do note that for physically accurate results we should refract the light <strong>again</strong> when it leaves the object; now we simply used single-sided refraction which is fine for most purposes. +</p> + +<h2>Dynamic environment maps</h2> +<p> + Right now we've been using a static combination of images as the skybox, which looks great, but it doesn't include the actual 3D scene with possibly moving objects. We didn't really notice this so far, because we only used a single object. If we had a mirror-like objects with multiple surrounding objects, only the skybox would be visible in the mirror as if it was the only object in the scene. +</p> + +<p> + Using framebuffers it is possible to create a texture of the scene for all 6 different angles from the object in question and store those in a cubemap each frame. We can then use this (dynamically generated) cubemap to create realistic reflection and refractive surfaces that include all other objects. This is called <def>dynamic environment mapping</def>, because we dynamically create a cubemap of an object's surroundings and use that as its environment map. +</p> + +<p> + While it looks great, it has one enormous disadvantage: we have to render the scene 6 times per object using an environment map, which is an enormous performance penalty on your application. Modern applications try to use the skybox as much as possible and where possible pre-render cubemaps wherever they can to still sort-of create dynamic environment maps. While dynamic environment mapping is a great technique, it requires a lot of clever tricks and hacks to get it working in an actual rendering application without too many performance drops. +</p> + + + +<!--<h2>Exercises</h2> +<ul> + <li>Try to introduce reflection maps into the model loader we created in the <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">model loading</a> chapters. You can find the upgraded nanosuit model with reflection maps included <a href="/objects/nanosuit_reflection.zip" target="_blank">here</a>. There are a few things to note though:</li> + <ul> + <li>Assimp doesn't really seem to like reflection maps in most object formats so we cheated a little by storing the reflection maps as <em>ambient maps</em>. You can then load the reflection maps by specifying <var>aiTextureType_AMBIENT</var> as the texture type when loading materials.</li> + <li>I sort of hastily created reflection map textures from the specular texture images, so the reflection maps won't map exactly to the model in some places :).</li> + <li>Since the model loader by itself already takes up 3 texture units in the shader, you'll have to bind the skybox to a 4th texture unit since we'll also sample from the skybox in the same shader.</li> + </ul> + <li>If you did things right it'll look something like <a href="/img/advanced/cubemaps_reflection_map.png" target="_blank">this</a>. + </li> +</ul> +--> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Depth-testing.html b/pub/Advanced-OpenGL/Depth-testing.html @@ -0,0 +1,619 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Depth testing</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Depth-testing</h1> +<p> + In the <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">coordinate systems</a> chapter we've rendered a 3D container and made use of a <def>depth buffer</def> to prevent triangles rendering in the front while they're supposed to be behind other triangles. In this chapter we're going to elaborate a bit more on those <def>depth values</def> the depth buffer (or z-buffer) stores and how it actually determines if a fragment is in front. +</p> + +<p> + The depth-buffer is a buffer that, just like the <def>color buffer</def> (that stores all the fragment colors: the visual output), stores information per fragment and has the same width and height as the color buffer. The depth buffer is automatically created by the windowing system and stores its depth values as <code>16</code>, <code>24</code> or <code>32</code> bit floats. In most systems you'll see a depth buffer with a precision of <code>24</code> bits. +</p> + +<p> + When depth testing is enabled, OpenGL tests the depth value of a fragment against the content of the depth buffer. OpenGL performs a depth test and if this test passes, the fragment is rendered and the depth buffer is updated with the new depth value. If the depth test fails, the fragment is discarded. +</p> + +<p> + Depth testing is done in screen space after the fragment shader has run (and after the stencil test which we'll get to in the <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing" target="_blank">next</a> chapter). The screen space coordinates relate directly to the viewport defined by OpenGL's <fun><function id='22'>glViewport</function></fun> function and can be accessed via GLSL's built-in <var>gl_FragCoord</var> variable in the fragment shader. The x and y components of <var>gl_FragCoord</var> represent the fragment's screen-space coordinates (with (0,0) being the bottom-left corner). The <var>gl_FragCoord</var> variable also contains a z-component which contains the depth value of the fragment. This z value is the value that is compared to the depth buffer's content. +</p> + +<note> + Today most GPUs support a hardware feature called <def>early depth testing</def>. Early depth testing allows the depth test to run before the fragment shader runs. Whenever it is clear a fragment isn't going to be visible (it is behind other objects) we can prematurely discard the fragment. +<br/><br/> +Fragment shaders are usually quite expensive so wherever we can avoid running them we should. A restriction on the fragment shader for early depth testing is that you shouldn't write to the fragment's depth value. If a fragment shader would write to its depth value, early depth testing is impossible; OpenGL won't be able to figure out the depth value beforehand. +</note> + +<p> + Depth testing is disabled by default so to enable depth testing we need to enable it with the <var>GL_DEPTH_TEST</var> option: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +</code></pre> + +<p> + Once enabled, OpenGL automatically stores fragments their z-values in the depth buffer if they passed the depth test and discards fragments if they failed the depth test accordingly. If you have depth testing enabled you should also clear the depth buffer before each frame using <var>GL_DEPTH_BUFFER_BIT</var>; otherwise you're stuck with the depth values from last frame: +</p> + +<pre><code> +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +</code></pre> + +<p> + There are certain scenarios imaginable where you want to perform the depth test on all fragments and discard them accordingly, but <strong>not</strong> update the depth buffer. Basically, you're (temporarily) using a <def>read-only</def> depth buffer. OpenGL allows us to disable writing to the depth buffer by setting its depth mask to <code>GL_FALSE</code>: +</p> + +<pre><code> +<function id='65'>glDepthMask</function>(GL_FALSE); +</code></pre> + +<p> + Note that this only has effect if depth testing is enabled. +</p> + +<h2>Depth test function</h2> +<p> + OpenGL allows us to modify the comparison operators it uses for the depth test. This allows us to control when OpenGL should pass or discard fragments and when to update the depth buffer. We can set the comparison operator (or depth function) by calling <fun><function id='66'>glDepthFunc</function></fun>: +</p> + +<pre><code> +<function id='66'>glDepthFunc</function>(GL_LESS); +</code></pre> + +<p> + The function accepts several comparison operators that are listed in the table below: +</p> + +<table> + <tr> + <th>Function</th> + <th>Description</th> + </tr> + <tr> + <td><code>GL_ALWAYS</code></td> + <td>The depth test always passes.</td> + </tr> + <tr> + <td><code>GL_NEVER</code></td> + <td>The depth test never passes.</td> + </tr> + <tr> + <td><code>GL_LESS</code></td> + <td>Passes if the fragment's depth value is less than the stored depth value.</td> + </tr> + <tr> + <td><code>GL_EQUAL</code></td> + <td>Passes if the fragment's depth value is equal to the stored depth value.</td> + </tr><tr> + <td><code>GL_LEQUAL</code></td> + <td>Passes if the fragment's depth value is less than or equal to the stored depth value.</td> + </tr> + <tr> + <td><code>GL_GREATER</code></td> + <td>Passes if the fragment's depth value is greater than the stored depth value.</td> + </tr> + <tr> + <td><code>GL_NOTEQUAL</code></td> + <td>Passes if the fragment's depth value is not equal to the stored depth value.</td> + </tr> + <tr> + <td><code>GL_GEQUAL</code></td> + <td>Passes if the fragment's depth value is greater than or equal to the stored depth value.</td> + </tr> +</table> + +<p> + By default the depth function <var>GL_LESS</var> is used that discards all the fragments that have a depth value higher than or equal to the current depth buffer's value. +</p> + +<p> + Let's show the effect that changing the depth function has on the visual output. We'll use a fresh code setup that displays a basic scene with two textured cubes sitting on a textured floor with no lighting. You can find the source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/1.1.depth_testing/depth_testing.cpp" target="_blank">here</a>. +</p> + +<p> + Within the source code we changed the depth function to <var>GL_ALWAYS</var>: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +<function id='66'>glDepthFunc</function>(GL_ALWAYS); +</code></pre> + +<p> + This simulates the same behavior we'd get if we didn't enable depth testing. The depth test always passes so the fragments that are drawn last are rendered in front of the fragments that were drawn before, even though they should've been at the front. Since we've drawn the floor plane last, the plane's fragments overwrite each of the container's previously written fragments: +</p> + +<img src="/img/advanced/depth_testing_func_always.png" class="clean" alt="Depth testing in OpenGL with GL_ALWAYS as depth function"/> + +<p> + Setting it all back to <var>GL_LESS</var> gives us the type of scene we're used to: +</p> + +<img src="/img/advanced/depth_testing_func_less.png" class="clean" alt="Depth testing in OpenGL with GL_LESS as depth function"/> + +<h2>Depth value precision</h2> +<p> + The depth buffer contains depth values between <code>0.0</code> and <code>1.0</code> and it compares its content with the z-values of all the objects in the scene as seen from the viewer. These z-values in view space can be any value between the projection-frustum's <code>near</code> and <code>far</code> plane. We thus need some way to transform these view-space z-values to the range of <code>[0,1]</code> and one way is to linearly transform them. The following (linear) equation transforms the z-value to a depth value between <code>0.0</code> and <code>1.0</code>: +</p> + +\begin{equation} F_{depth} = \frac{z - near}{far - near} \end{equation} + +<p> + Here \(near\) and \(far\) are the <em>near</em> and <em>far</em> values we used to provide to the projection matrix to set the visible frustum (see <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">coordinate Systems</a>). The equation takes a depth value \(z\) within the frustum and transforms it to the range <code>[0,1]</code>. The relation between the z-value and its corresponding depth value is presented in the following graph: +</p> + +<img src="/img/advanced/depth_linear_graph.png" class="clean" alt="Graph of depth values in OpenGL as a linear function"/> + +<note> + Note that all equations give a depth value close to <code>0.0</code> when the object is close by and a depth value close to <code>1.0</code> when the object is close to the far plane. +</note> + +<p> + In practice however, a <def>linear depth buffer</def> like this is almost never used. Because of projection properties a non-linear depth equation is used that is proportional to 1/z. The result is that we get enormous precision when z is small and much less precision when z is far away. +</p> + +<p> + Since the non-linear function is proportional to 1/z, z-values between <code>1.0</code> and <code>2.0</code> would result in depth values between <code>1.0</code> and <code>0.5</code> which is half of the [0,1] range, giving us enormous precision at small z-values. Z-values between <code>50.0</code> and <code>100.0</code> would account for only 2% of the [0,1] range. Such an equation, that also takes near and far distances into account, is given below: +</p> + +\begin{equation} F_{depth} = \frac{1/z - 1/near}{1/far - 1/near} \end{equation} + +<p> + Don't worry if you don't know exactly what is going on with this equation. The important thing to remember is that the values in the depth buffer are not linear in clip-space (they are linear in view-space before the projection matrix is applied). A value of <code>0.5</code> in the depth buffer does not mean the pixel's z-value is halfway in the frustum; the z-value of the vertex is actually quite close to the near plane! You can see the non-linear relation between the z-value and the resulting depth buffer's value in the following graph: +</p> + +<img src="/img/advanced/depth_non_linear_graph.png" class="clean" alt="Graph of depth values in OpenGL as a non-linear function"/> + +<p> + As you can see, the depth values are greatly determined by the small z-values giving us large depth precision to the objects close by. The equation to transform z-values (from the viewer's perspective) is embedded within the projection matrix so when we transform vertex coordinates from view to clip, and then to screen-space the non-linear equation is applied. +</p> + +<p> + The effect of this non-linear equation quickly becomes apparent when we try to visualize the depth buffer. +</p> + +<h2>Visualizing the depth buffer</h2> +<p> + We know that the z-value of the built-in <var>gl_FragCoord</var> vector in the fragment shader contains the depth value of that particular fragment. If we were to output this depth value of the fragment as a color we could display the depth values of all the fragments in the scene: +</p> + +<pre><code> +void main() +{ + FragColor = vec4(vec3(gl_FragCoord.z), 1.0); +} +</code></pre> + +<p> + If you'd then run the program you'll probably notice that everything is white, making it look like all of our depth values are the maximum depth value of <code>1.0</code>. So why aren't any of the depth values closer to <code>0.0</code> and thus darker? +</p> + +<p> + In the previous section we described that depth values in screen space are non-linear e.g. they have a very high precision for small z-values and a low precision for large z-values. The depth value of the fragment increases rapidly over distance so almost all the vertices have values close to <code>1.0</code>. If we were to carefully move really close to an object you may eventually see the colors getting darker, their z-values becoming smaller: +</p> + +<img src="/img/advanced/depth_testing_visible_depth.png" class="clean" alt="Depth buffer visualized in OpenGL and GLSL"/> + +<p> + This clearly shows the non-linearity of the depth value. Objects close by have a much larger effect on the depth value than objects far away. Only moving a few inches can result in the colors going from dark to completely white. +</p> + +<p> + We can however, transform the non-linear depth values of the fragment back to its linear sibling. To achieve this we basically need to reverse the process of projection for the depth values alone. This means we have to first re-transform the depth values from the range <code>[0,1]</code> to normalized device coordinates in the range <code>[-1,1]</code>. Then we want to reverse the non-linear equation (equation 2) as done in the projection matrix and apply this inversed equation to the resulting depth value. The result is then a linear depth value. +</p> + +<p> + First we transform the depth value to NDC which is not too difficult: +</p> + +<pre><code> +float ndc = depth * 2.0 - 1.0; +</code></pre> + +<p> + We then take the resulting <var>ndc</var> value and apply the inverse transformation to retrieve its linear depth value: +</p> + +<pre><code> +float linearDepth = (2.0 * near * far) / (far + near - ndc * (far - near)); +</code></pre> + +<p> + This equation is derived from the projection matrix for non-linearizing the depth values, returning depth values between <var>near</var> and <var>far</var>. This <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html" target="_blank">math-heavy article</a> explains the projection matrix in enormous detail for the interested reader; it also shows where the equations come from. +</p> + +<p> + The complete fragment shader that transforms the non-linear depth in screen-space to a linear depth value is then as follows: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +float near = 0.1; +float far = 100.0; + +float LinearizeDepth(float depth) +{ + float z = depth * 2.0 - 1.0; // back to NDC + return (2.0 * near * far) / (far + near - z * (far - near)); +} + +void main() +{ + float depth = LinearizeDepth(gl_FragCoord.z) / far; // divide by far for demonstration + FragColor = vec4(vec3(depth), 1.0); +} +</code></pre> + +<p> + Because the linearized depth values range from <var>near</var> to <var>far</var> most of its values will be above <code>1.0</code> and displayed as completely white. By dividing the linear depth value by <var>far</var> in the <fun>main</fun> function we convert the linear depth value to the range [<code>0</code>, <code>1</code>]. This way we can gradually see the scene become brighter the closer the fragments are to the projection frustum's far plane, which works better for visualization purposes. +</p> + +<p> + If we'd now run the application we get depth values that are linear over distance. Try moving around the scene to see the depth values change in a linear fashion. +</p> + +<img src="/img/advanced/depth_testing_visible_linear.png" class="clean" alt="Depth buffer visualized in OpenGL and GLSL as linear values"/> + +<p> + The colors are mostly black because the depth values range linearly from the <code>near</code> plane (<code>0.1</code>) to the <code>far</code> plane (<code>100</code>) which is still quite far away from us. The result is that we're relatively close to the near plane and therefore get lower (darker) depth values. +</p> + +<h2>Z-fighting</h2> +<p> + A common visual artifact may occur when two planes or triangles are so closely aligned to each other that the depth buffer does not have enough precision to figure out which one of the two shapes is in front of the other. The result is that the two shapes continually seem to switch order which causes weird glitchy patterns. This is called <def>z-fighting</def>, because it looks like the shapes are fighting over who gets on top. +</p> + +<p> + In the scene we've been using so far there are a few spots where z-fighting can be noticed. The containers were placed at the exact height of the floor which means the bottom plane of the container is coplanar with the floor plane. The depth values of both planes are then the same so the resulting depth test has no way of figuring out which is the right one. +</p> + +<p> + If you move the camera inside one of the containers the effects are clearly visible, the bottom part of the container is constantly switching between the container's plane and the floor's plane in a zigzag pattern: +</p> + +<img src="/img/advanced/depth_testing_z_fighting.png" class="clean" alt="Demonstration of Z-fighting in OpenGL"/> + +<p> + Z-fighting is a common problem with depth buffers and it's generally more noticeable when objects are further away (because the depth buffer has less precision at larger z-values). Z-fighting can't be completely prevented, but there are a few tricks that will help to mitigate or completely prevent z-fighting in your scene. +</p> + +<h3>Prevent z-fighting</h3> +<p> + The first and most important trick is <em>never place objects too close to each other in a way that some of their triangles closely overlap</em>. By creating a small offset between two objects you can completely remove z-fighting between the two objects. In the case of the containers and the plane we could've easily moved the containers slightly upwards in the positive y direction. The small change of the container's positions would probably not be noticeable at all and would completely reduce the z-fighting. However, this requires manual intervention of each of the objects and thorough testing to make sure no objects in a scene produce z-fighting. +</p> + +<p> + A second trick is to <em>set the near plane as far as possible</em>. In one of the previous sections we've discussed that precision is extremely large when close to the <code>near</code> plane so if we move the <code>near</code> plane away from the viewer, we'll have significantly greater precision over the entire frustum range. However, setting the <code>near</code> plane too far could cause clipping of near objects so it is usually a matter of tweaking and experimentation to figure out the best <code>near</code> distance for your scene. +</p> + +<p> + Another great trick at the cost of some performance is to <em>use a higher precision depth buffer</em>. Most depth buffers have a precision of <code>24</code> bits, but most GPUs nowadays support <code>32</code> bit depth buffers, increasing the precision by a significant amount. So at the cost of some performance you'll get much more precision with depth testing, reducing z-fighting. +</p> + +<p> + The 3 techniques we've discussed are the most common and easy-to-implement anti z-fighting techniques. There are some other techniques out there that require a lot more work and still won't completely disable z-fighting. Z-fighting is a common issue, but if you use the proper combination of the listed techniques you probably won't need to deal with z-fighting that much. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Face-culling.html b/pub/Advanced-OpenGL/Face-culling.html @@ -0,0 +1,468 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Face culling</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Face-culling</h1> +<p> + Try mentally visualizing a 3D cube and count the maximum number of faces you'll be able to see from any direction. If your imagination is not too creative you probably ended up with a maximum number of 3. You can view a cube from any position and/or direction, but you would never be able to see more than 3 faces. So why would we waste the effort of drawing those other 3 faces that we can't even see. If we could discard those in some way we would save more than 50% of this cube's total fragment shader runs! +</p> + +<note> + We say <em>more than 50%</em> instead of 50%, because from certain angles only 2 or even 1 face could be visible. In that case we'd save <strong>more</strong> than 50%. +</note> + +<p> + This is a really great idea, but there's one problem we need to solve: how do we know if a face of an object is not visible from the viewer's point of view? If we imagine any closed shape, each of its faces has two sides. Each side would either <em>face</em> the user or show its back to the user. What if we could only render the faces that are <em>facing</em> the viewer? +</p> + +<p> + This is exactly what <def>face culling</def> does. OpenGL checks all the faces that are <def>front facing</def> towards the viewer and renders those while discarding all the faces that are <def>back facing</def>, saving us a lot of fragment shader calls. We do need to tell OpenGL which of the faces we use are actually the front faces and which faces are the back faces. OpenGL uses a clever trick for this by analyzing the <def>winding order</def> of the vertex data. +</p> + +<h2>Winding order</h2> +<p> + When we define a set of triangle vertices we're defining them in a certain winding order that is either <def>clockwise</def> or <def>counter-clockwise</def>. Each triangle consists of 3 vertices and we specify those 3 vertices in a winding order as seen from the center of the triangle. +</p> + +<img src="/img/advanced/faceculling_windingorder.png" class="clean" alt="Winding order of OpenGL triangles"/> + +<p> + As you can see in the image we first define vertex <code>1</code> and from there we can choose whether the next vertex is <code>2</code> or <code>3</code>. This choice defines the winding order of this triangle. The following code illustrates this: +</p> + +<pre><code> +float vertices[] = { + // clockwise + vertices[0], // vertex 1 + vertices[1], // vertex 2 + vertices[2], // vertex 3 + // counter-clockwise + vertices[0], // vertex 1 + vertices[2], // vertex 3 + vertices[1] // vertex 2 +}; +</code></pre> + +<p> + Each set of 3 vertices that form a triangle primitive thus contain a winding order. OpenGL uses this information when rendering your primitives to determine if a triangle is a <def>front-facing</def> or a <def>back-facing</def> triangle. By default, triangles defined with counter-clockwise vertices are processed as front-facing triangles. +</p> + +<p> + When defining your vertex order you visualize the corresponding triangle as if it was facing you, so each triangle that you're specifying should be counter-clockwise as if you're directly facing that triangle. The cool thing about specifying all your vertices like this is that the actual winding order is calculated at the rasterization stage, so when the vertex shader has already run. The vertices are then seen as from the <strong>viewer's point of view</strong>. +</p> + +<p> + All the triangle vertices that the viewer is then facing are indeed in the correct winding order as we specified them, but the vertices of the triangles at the other side of the cube are now rendered in such a way that their winding order becomes reversed. The result is that the triangles we're facing are seen as front-facing triangles and the triangles at the back are seen as back-facing triangles. The following image shows this effect: +</p> + +<img src="/img/advanced/faceculling_frontback.png" class="clean" alt="Image of viewer seeing front or back facing triangles"/> + +<p> + In the vertex data we defined both triangles in counter-clockwise order (the front and back triangle as 1, 2, 3). However, from the viewer's direction the back triangle is rendered clockwise if we draw it in the order of 1, 2 and 3 from the viewer's current point of view. Even though we specified the back triangle in counter-clockwise order, it is now rendered in a clockwise order. This is exactly what we want to <def>cull</def> (discard) non-visible faces! +</p> + +<h2>Face culling</h2> +<p> + At the start of the chapter we said that OpenGL is able to discard triangle primitives if they're rendered as back-facing triangles. Now that we know how to set the winding order of the vertices we can start using OpenGL's <def>face culling</def> option which is disabled by default. +</p> + +<p> + The cube vertex data we used in the previous chapters wasn't defined with the counter-clockwise winding order in mind, so I updated the vertex data to reflect a counter-clockwise winding order which you can copy from <a href="/code_viewer.php?code=advanced/faceculling_vertexdata" target="_blank">here</a>. It's a good practice to try and visualize that these vertices are indeed all defined in a counter-clockwise order for each triangle. +</p> + +<p> + To enable face culling we only have to enable OpenGL's <var>GL_CULL_FACE</var> option: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_CULL_FACE); +</code></pre> + +<p> + From this point on, all the faces that are not front-faces are discarded (try flying inside the cube to see that all inner faces are indeed discarded). Currently we save over 50% of performance on rendering fragments if OpenGL decides to render the back faces first (otherwise depth testing would've discarded them already). Do note that this only really works with closed shapes like a cube. We do have to disable face culling again when we draw the grass leaves from the <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">previous</a> chapter, since their front <strong>and</strong> back face should be visible. +</p> + +<p> + OpenGL allows us to change the type of face we want to cull as well. What if we want to cull front faces and not the back faces? We can define this behavior with <fun><function id='74'>glCullFace</function></fun>: +</p> + +<pre><code> +<function id='74'>glCullFace</function>(GL_FRONT); +</code></pre> + +<p> + The <fun><function id='74'>glCullFace</function></fun> function has three possible options: +</p> + +<ul> + <li><var>GL_BACK</var>: Culls only the back faces.</li> + <li><var>GL_FRONT</var>: Culls only the front faces.</li> + <li><var>GL_FRONT_AND_BACK</var>: Culls both the front and back faces.</li> +</ul> + +<p> + The initial value of <fun><function id='74'>glCullFace</function></fun> is <var>GL_BACK</var>. We can also tell OpenGL we'd rather prefer clockwise faces as the front-faces instead of counter-clockwise faces via <fun><function id='75'>glFrontFace</function></fun>: +</p> + +<pre><code> +<function id='75'>glFrontFace</function>(GL_CCW); +</code></pre> + +<p> + The default value is <var>GL_CCW</var> that stands for counter-clockwise ordering with the other option being <var>GL_CW</var> which (obviously) stands for clockwise ordering. +</p> + +<p> + As a simple test we could reverse the winding order by telling OpenGL that the front-faces are now determined by a clockwise ordering instead of a counter-clockwise ordering: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_CULL_FACE); +<function id='74'>glCullFace</function>(GL_BACK); +<function id='75'>glFrontFace</function>(GL_CW); +</code></pre> + +<p> + The result is that only the back faces are rendered: +</p> + +<img src="/img/advanced/faceculling_reverse.png" class="clean" alt="Image of faceculling with clockwise ordering, only culling the front faces"/> + +<p> + Note that you can create the same effect by culling front faces with the default counter-clockwise winding order: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_CULL_FACE); +<function id='74'>glCullFace</function>(GL_FRONT); +</code></pre> + +<p> + As you can see, face culling is a great tool for increasing performance of your OpenGL applications with minimal effort; especially as all 3D applications export models with consistent winding orders (CCW by default). You do have to keep track of the objects that will actually benefit from face culling and which objects shouldn't be culled at all. +</p> + +<h2>Exercises</h2> +<ul> + <li>Can you re-define the vertex data by specifying each triangle in clockwise order and then render the scene with clockwise triangles set as the front faces: <a href="/code_viewer.php?code=advanced/faceculling-exercise1" target="_blank">solution</a></li> +</ul> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Framebuffers.html b/pub/Advanced-OpenGL/Framebuffers.html @@ -0,0 +1,899 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Framebuffers</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Framebuffers</h1> +<p> + So far we've used several types of screen buffers: a color buffer for writing color values, a depth buffer to write and test depth information, and finally a stencil buffer that allows us to discard certain fragments based on some condition. The combination of these buffers is stored somewhere in GPU memory and is called a <def>framebuffer</def>. OpenGL gives us the flexibility to define our own framebuffers and thus define our own color (and optionally a depth and stencil) buffer. +</p> + +<p> + The rendering operations we've done so far were all done on top of the render buffers attached to the <def>default framebuffer</def>. The default framebuffer is created and configured when you create your window (GLFW does this for us). By creating our own framebuffer we can get an additional target to render to. +</p> + +<p> + The application of framebuffers may not immediately make sense, but rendering your scene to a different framebuffer allows us to use that result to create mirrors in a scene, or do cool post-processing effects for example. First we'll discuss how they actually work and then we'll use them by implementing those cool post-processing effects. +</p> + +<h2>Creating a framebuffer</h2> +<p> + Just like any other object in OpenGL we can create a framebuffer object (abbreviated to FBO) by using a function called <fun><function id='76'>glGenFramebuffers</function></fun>: +</p> + +<pre class="cpp"><code> +unsigned int fbo; +<function id='76'>glGenFramebuffers</function>(1, &fbo); +</code></pre> + +<p> + This pattern of object creation and usage is something we've seen dozens of times now so their usage functions are similar to all the other object's we've seen: first we create a framebuffer object, bind it as the active framebuffer, do some operations, and unbind the framebuffer. To bind the framebuffer we use <fun><function id='77'>glBindFramebuffer</function></fun>: +</p> + +<pre><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, fbo); +</code></pre> + +<p> + By binding to the <var>GL_FRAMEBUFFER</var> target all the next <em>read</em> and <em>write</em> framebuffer operations will affect the currently bound framebuffer. It is also possible to bind a framebuffer to a read or write target specifically by binding to <var>GL_READ_FRAMEBUFFER</var> or <var>GL_DRAW_FRAMEBUFFER</var> respectively. The framebuffer bound to <var>GL_READ_FRAMEBUFFER</var> is then used for all read operations like <fun><function id='78'>glReadPixels</function></fun> and the framebuffer bound to <var>GL_DRAW_FRAMEBUFFER</var> is used as the destination for rendering, clearing and other write operations. Most of the times you won't need to make this distinction though and you generally bind to both with <var>GL_FRAMEBUFFER</var>. +</p> + +<p> + Unfortunately, we can't use our framebuffer yet because it is not <def>complete</def>. For a framebuffer to be complete the following requirements have to be satisfied: +</p> + +<ul> + <li>We have to attach at least one buffer (color, depth or stencil buffer).</li> + <li>There should be at least one color attachment.</li> + <li>All attachments should be complete as well (reserved memory).</li> + <li>Each buffer should have the same number of samples.</li> +</ul> + +<p> + Don't worry if you don't know what samples are, we'll get to those in a <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing" target="_blank">later</a> chapter. +</p> + +<p> + From the requirements it should be clear that we need to create some kind of attachment for the framebuffer and attach this attachment to the framebuffer. After we've completed all requirements we can check if we actually successfully completed the framebuffer by calling <fun><function id='79'>glCheckFramebufferStatus</function></fun> with <var>GL_FRAMEBUFFER</var>. It then checks the currently bound framebuffer and returns any of <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/%67lCheckFramebufferStatus.xhtml" target="_blank">these</a> values found in the specification. If it returns <var>GL_FRAMEBUFFER_COMPLETE</var> we're good to go: +</p> + +<pre><code> +if(<function id='79'>glCheckFramebufferStatus</function>(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) + // execute victory dance +</code></pre> + +<p> + All subsequent rendering operations will now render to the attachments of the currently bound framebuffer. Since our framebuffer is not the default framebuffer, the rendering commands will have no impact on the visual output of your window. For this reason it is called <def>off-screen rendering</def> when rendering to a different framebuffer. If you want all rendering operations to have a visual impact again on the main window we need to make the default framebuffer active by binding to <code>0</code>: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + When we're done with all framebuffer operations, do not forget to delete the framebuffer object: +</p> + +<pre class="cpp"><code> +<function id='80'>glDeleteFramebuffers</function>(1, &fbo); +</code></pre> + +<p> + Now before the completeness check is executed we need to attach one or more attachments to the framebuffer. An <def>attachment</def> is a memory location that can act as a buffer for the framebuffer, think of it as an image. When creating an attachment we have two options to take: textures or <def>renderbuffer</def> objects. +</p> + +<h3>Texture attachments</h3> +<p> + When attaching a texture to a framebuffer, all rendering commands will write to the texture as if it was a normal color/depth or stencil buffer. The advantage of using textures is that the render output is stored inside the texture image that we can then easily use in our shaders. +</p> + +<p> + Creating a texture for a framebuffer is roughly the same as creating a normal texture: +</p> + +<pre class="cpp"><code> +unsigned int texture; +<function id='50'>glGenTextures</function>(1, &texture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); + +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<p> + The main differences here is that we set the dimensions equal to the screen size (although this is not required) and we pass <code>NULL</code> as the texture's <code>data</code> parameter. For this texture, we're only allocating memory and not actually filling it. Filling the texture will happen as soon as we render to the framebuffer. Also note that we do not care about any of the wrapping methods or mipmapping since we won't be needing those in most cases. +</p> + +<note> + If you want to render your whole screen to a texture of a smaller or larger size you need to call <fun><function id='22'>glViewport</function></fun> again (before rendering to your framebuffer) with the new dimensions of your texture, otherwise render commands will only fill part of the texture. +</note> + +<p> + Now that we've created a texture, the last thing we need to do is actually attach it to the framebuffer: +</p> + +<pre class="cpp"><code> +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); +</code></pre> + +<p> + The <fun><function id='81'>glFrameBufferTexture2D</function></fun> function has the following parameters: +</p> + +<ul> + <li><code>target</code>: the framebuffer type we're targeting (draw, read or both).</li> + <li><code>attachment</code>: the type of attachment we're going to attach. Right now we're attaching a color attachment. Note that the <code>0</code> at the end suggests we can attach more than 1 color attachment. We'll get to that in a later chapter.</li> + <li><code>textarget</code>: the type of the texture you want to attach.</li> + <li><code>texture</code>: the actual texture to attach.</li> + <li><code>level</code>: the mipmap level. We keep this at <code>0</code>.</li> +</ul> + +<p> + Next to the color attachments we can also attach a depth and a stencil texture to the framebuffer object. To attach a depth attachment we specify the attachment type as <var>GL_DEPTH_ATTACHMENT</var>. Note that the texture's <def>format</def> and <def>internalformat</def> type should then become <var>GL_DEPTH_COMPONENT</var> to reflect the depth buffer's storage format. To attach a stencil buffer you use <var>GL_STENCIL_ATTACHMENT</var> as the second argument and specify the texture's formats as <var>GL_STENCIL_INDEX</var>. +</p> + +<p> + It is also possible to attach both a depth buffer and a stencil buffer as a single texture. Each 32 bit value of the texture then contains 24 bits of depth information and 8 bits of stencil information. To attach a depth and stencil buffer as one texture we use the <var>GL_DEPTH_STENCIL_ATTACHMENT</var> type and configure the texture's formats to contain combined depth and stencil values. An example of attaching a depth and stencil buffer as one texture to the framebuffer is given below: +</p> + + +<pre class="cpp"><code> +<function id='52'>glTexImage2D</function>( + GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0, + GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL +); + +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0); +</code></pre> + +<h3>Renderbuffer object attachments</h3> +<p> + <def>Renderbuffer objects</def> were introduced to OpenGL after textures as a possible type of framebuffer attachment, Just like a texture image, a renderbuffer object is an actual buffer e.g. an array of bytes, integers, pixels or whatever. However, a renderbuffer object can not be directly read from. This gives it the added advantage that OpenGL can do a few memory optimizations that can give it a performance edge over textures for off-screen rendering to a framebuffer. +</p> + +<p> + Renderbuffer objects store all the render data directly into their buffer without any conversions to texture-specific formats, making them faster as a writeable storage medium. You cannot read from them directly, but it is possible to read from them via the slow <fun><function id='78'>glReadPixels</function></fun>. This returns a specified area of pixels from the currently bound framebuffer, but not directly from the attachment itself. +</p> + +<p> + Because their data is in a native format they are quite fast when writing data or copying data to other buffers. Operations like switching buffers are therefore quite fast when using renderbuffer objects. The <fun><function id='24'>glfwSwapBuffers</function></fun> function we've been using at the end of each frame may as well be implemented with renderbuffer objects: we simply write to a renderbuffer image, and swap to the other one at the end. Renderbuffer objects are perfect for these kind of operations. +</p> + +<p> + Creating a renderbuffer object looks similar to the framebuffer's code: +</p> + +<pre class="cpp"><code> +unsigned int rbo; +<function id='82'>glGenRenderbuffers</function>(1, &rbo); +</code></pre> + +<p> + And similarly we want to bind the renderbuffer object so all subsequent renderbuffer operations affect the current <var>rbo</var>: +</p> + +<pre><code> +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, rbo); +</code></pre> + +<p> + Since renderbuffer objects are write-only they are often used as depth and stencil attachments, since most of the time we don't really need to read values from them, but we do care about depth and stencil testing. We <strong>need</strong> the depth and stencil values for testing, but don't need to <em>sample</em> these values so a renderbuffer object suits this perfectly. When we're not sampling from these buffers, a renderbuffer object is generally preferred. +</p> + +<p> + Creating a depth and stencil renderbuffer object is done by calling the <fun><function id='88'>glRenderbufferStorage</function></fun> function: +</p> + +<pre class="cpp"><code> +<function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600); +</code></pre> + +<p> + Creating a renderbuffer object is similar to texture objects, the difference being that this object is specifically designed to be used as a framebuffer attachment, instead of a general purpose data buffer like a texture. Here we've chosen <var>GL_DEPTH24_STENCIL8</var> as the internal format, which holds both the depth and stencil buffer with 24 and 8 bits respectively. +</p> + +<p> + The last thing left to do is to actually attach the renderbuffer object: +</p> + +<pre><code> +<function id='89'>glFramebufferRenderbuffer</function>(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); +</code></pre> + +<p> + Renderbuffer objects can be more efficient for use in your off-screen render projects, but it is important to realize when to use renderbuffer objects and when to use textures. The general rule is that if you never need to sample data from a specific buffer, it is wise to use a renderbuffer object for that specific buffer. If you need to sample data from a specific buffer like colors or depth values, you should use a texture attachment instead. +</p> + +<h2>Rendering to a texture</h2> +<p> + Now that we know how framebuffers (sort of) work it's time to put them to good use. We're going to render the scene into a color texture attached to a framebuffer object we created and then draw this texture over a simple quad that spans the whole screen. The visual output is then exactly the same as without a framebuffer, but this time it's all printed on top of a single quad. Now why is this useful? In the next section we'll see why. +</p> + +<p> + First thing to do is to create an actual framebuffer object and bind it, this is all relatively straightforward: +</p> + +<pre class="cpp"><code> +unsigned int framebuffer; +<function id='76'>glGenFramebuffers</function>(1, &framebuffer); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, framebuffer); +</code></pre> + +<p> + Next we create a texture image that we attach as a color attachment to the framebuffer. We set the texture's dimensions equal to the width and height of the window and keep its data uninitialized: +</p> + +<pre><code> +// generate texture +unsigned int texColorBuffer; +<function id='50'>glGenTextures</function>(1, &texColorBuffer); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texColorBuffer); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); + +// attach it to currently bound framebuffer object +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0); +</code></pre> + +<p> + We also want to make sure OpenGL is able to do depth testing (and optionally stencil testing) so we have to make sure to add a depth (and stencil) attachment to the framebuffer. Since we'll only be sampling the color buffer and not the other buffers we can create a renderbuffer object for this purpose. +</p> + +<p> + Creating a renderbuffer object isn't too hard. The only thing we have to remember is that we're creating it as a depth <strong>and</strong> stencil attachment renderbuffer object. We set its <em>internal format</em> to <var>GL_DEPTH24_STENCIL8</var> which is enough precision for our purposes: +</p> + +<pre class="cpp"><code> +unsigned int rbo; +<function id='82'>glGenRenderbuffers</function>(1, &rbo); +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, rbo); +<function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600); +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, 0); +</code></pre> + +<p> + Once we've allocated enough memory for the renderbuffer object we can unbind the renderbuffer. +</p> + +<p> + Then, as a final step before we complete the framebuffer, we attach the renderbuffer object to the depth <strong>and</strong> stencil attachment of the framebuffer: +</p> + +<pre><code> +<function id='89'>glFramebufferRenderbuffer</function>(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); +</code></pre> + +<p> + Then we want to check if the framebuffer is complete and if it's not, we print an error message. +</p> + +<pre class="cpp"><code> +if(<function id='79'>glCheckFramebufferStatus</function>(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout &lt;&lt; "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" &lt;&lt; std::endl; +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + Be sure to unbind the framebuffer to make sure we're not accidentally rendering to the wrong framebuffer. +</p> + +<p> + Now that the framebuffer is complete, all we need to do to render to the framebuffer's buffers instead of the default framebuffers is to simply bind the framebuffer object. All subsequent render commands will then influence the currently bound framebuffer. All the depth and stencil operations will also read from the currently bound framebuffer's depth and stencil attachments if they're available. If you were to omit a depth buffer for example, all depth testing operations will no longer work. +</p> + +<p> + So, to draw the scene to a single texture we'll have to take the following steps: +</p> + +<ol> + <li>Render the scene as usual with the new framebuffer bound as the active framebuffer.</li> + <li>Bind to the default framebuffer.</li> + <li>Draw a quad that spans the entire screen with the new framebuffer's color buffer as its texture.</li> +</ol> + +<p> + We'll render the same scene we've used in the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter, but this time with the old-school <a href="https://learnopengl.com/img/textures/container.jpg" target="_blank">container</a> texture. +</p> + +<p> + To render the quad we're going to create a fresh set of simple shaders. We're not going to include fancy matrix transformations since we'll be supplying the <a href="/code_viewer.php?code=advanced/framebuffers_quad_vertices" target="_blank">vertex coordinates as normalized device coordinates</a> so we can directly forward them as output of the vertex shader. The vertex shader looks like this: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoords; + +out vec2 TexCoords; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); + TexCoords = aTexCoords; +} +</code></pre> + +<p> + Nothing too fancy. The fragment shader is even more basic since the only thing we have to do is sample from a texture: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D screenTexture; + +void main() +{ + FragColor = texture(screenTexture, TexCoords); +} +</code></pre> + +<p> + It is then up to you to create and configure a VAO for the screen quad. A single render iteration of the framebuffer procedure has the following structure: +</p> + +<pre><code> +// first pass +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, framebuffer); +<function id='13'><function id='10'>glClear</function>Color</function>(0.1f, 0.1f, 0.1f, 1.0f); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // we're not using the stencil buffer now +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +DrawScene(); + +// second pass +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); // back to default +<function id='13'><function id='10'>glClear</function>Color</function>(1.0f, 1.0f, 1.0f, 1.0f); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT); + +screenShader.use(); +<function id='27'>glBindVertexArray</function>(quadVAO); +glDisable(GL_DEPTH_TEST); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, textureColorbuffer); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +</code></pre> + +<p> + There are a few things to note. First, since each framebuffer we're using has its own set of buffers, we want to clear each of those buffers with the appropriate bits set by calling <fun><function id='10'>glClear</function></fun>. Second, when drawing the quad, we're disabling depth testing since we want to make sure the quad always renders in front of everything else; we'll have to enable depth testing again when we draw the normal scene though. +</p> + +<p> + There are quite some steps that could go wrong here, so if you have no output, try to debug where possible and re-read the relevant sections of the chapter. If everything did work out successfully you'll get a visual result that looks like this: +</p> + +<img src="/img/advanced/framebuffers_screen_texture.png" alt="An image of a 3D scene in OpenGL rendered to a texture via framebuffers"/> + +<p> + The left shows the visual output, exactly the same as we've seen in the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">depth testing</a> chapter, but this time rendered on a simple quad. If we render the scene in wireframe it's obvious we've only drawn a single quad in the default framebuffer. +</p> + +<p> + You can find the source code of the application <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/5.1.framebuffers/framebuffers.cpp" target="_blank">here</a>. +</p> + +<p> + So what was the use of this again? Well, because we can now freely access each of the pixels of the completely rendered scene as a single texture image, we can create some interesting effects in the fragment shader. +</p> + +<h1>Post-processing</h1> +<p> + Now that the entire scene is rendered to a single texture we can create cool <def>post-processing</def> effects by manipulating the scene texture. In this section we'll show you some of the more popular post-processing effects and how you may create your own with some added creativity. +</p> + +<p> + Let's start with one of the simplest post-processing effects. +</p> + +<h3>Inversion</h3> +<p> + We have access to each of the colors of the render output so it's not so hard to return the inverse of these colors in the fragment shader. We can take the color of the screen texture and inverse it by subtracting it from <code>1.0</code>: +</p> + +<pre><code> +void main() +{ + FragColor = vec4(vec3(1.0 - texture(screenTexture, TexCoords)), 1.0); +} +</code></pre> + +<p> + While inversion is a relatively simple post-processing effect it already creates funky results: +</p> + +<img src="/img/advanced/framebuffers_inverse.png" class="clean" alt="Post-processing image of a 3D scene in OpenGL with inversed colors"/> + +<p> + The entire scene now has all its colors inversed with a single line of code in the fragment shader. Pretty cool huh? +</p> + +<h3>Grayscale</h3> +<p> + Another interesting effect is to remove all colors from the scene except the white, gray and black colors; effectively grayscaling the entire image. An easy way to do this is by taking all the color components and averaging their results: +</p> + +<pre><code> +void main() +{ + FragColor = texture(screenTexture, TexCoords); + float average = (FragColor.r + FragColor.g + FragColor.b) / 3.0; + FragColor = vec4(average, average, average, 1.0); +} +</code></pre> + +<p> + This already creates pretty good results, but the human eye tends to be more sensitive to green colors and the least to blue. So to get the most physically accurate results we'll need to use weighted channels: +</p> + +<pre><code> +void main() +{ + FragColor = texture(screenTexture, TexCoords); + float average = 0.2126 * FragColor.r + 0.7152 * FragColor.g + 0.0722 * FragColor.b; + FragColor = vec4(average, average, average, 1.0); +} +</code></pre> + +<img src="/img/advanced/framebuffers_grayscale.png" class="clean" alt="Post-processing image of a 3D scene in OpenGL with grayscale colors"/> + +<p> + You probably won't notice the difference right away, but with more complicated scenes, such a weighted grayscaling effect tends to be more realistic. +</p> + +<h2>Kernel effects</h2> +<p> + Another advantage about doing post-processing on a single texture image is that we can sample color values from other parts of the texture not specific to that fragment. We could for example take a small area around the current texture coordinate and sample multiple texture values around the current texture value. We can then create interesting effects by combining them in creative ways. +</p> + +<p> + A <def>kernel</def> (or convolution matrix) is a small matrix-like array of values centered on the current pixel that multiplies surrounding pixel values by its kernel values and adds them all together to form a single value. We're adding a small offset to the texture coordinates in surrounding directions of the current pixel and combine the results based on the kernel. An example of a kernel is given below: +</p> + +\[\begin{bmatrix}2 & 2 & 2 \\ 2 & -15 & 2 \\ 2 & 2 & 2 \end{bmatrix}\] + +<p> + This kernel takes 8 surrounding pixel values and multiplies them by <code>2</code> and the current pixel by <code>-15</code>. This example kernel multiplies the surrounding pixels by several weights determined in the kernel and balances the result by multiplying the current pixel by a large negative weight. +</p> + +<note> + Most kernels you'll find over the internet all sum up to <code>1</code> if you add all the weights together. If they don't add up to <code>1</code> it means that the resulting texture color ends up brighter or darker than the original texture value. +</note> + +<p> + Kernels are an extremely useful tool for post-processing since they're quite easy to use and experiment with, and a lot of examples can be found online. We do have to slightly adapt the fragment shader a bit to actually support kernels. We make the assumption that each kernel we'll be using is a 3x3 kernel (which most kernels are): +</p> + +<pre><code> +const float offset = 1.0 / 300.0; + +void main() +{ + vec2 offsets[9] = vec2[]( + vec2(-offset, offset), // top-left + vec2( 0.0f, offset), // top-center + vec2( offset, offset), // top-right + vec2(-offset, 0.0f), // center-left + vec2( 0.0f, 0.0f), // center-center + vec2( offset, 0.0f), // center-right + vec2(-offset, -offset), // bottom-left + vec2( 0.0f, -offset), // bottom-center + vec2( offset, -offset) // bottom-right + ); + + float kernel[9] = float[]( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ); + + vec3 sampleTex[9]; + for(int i = 0; i &lt; 9; i++) + { + sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i])); + } + vec3 col = vec3(0.0); + for(int i = 0; i &lt; 9; i++) + col += sampleTex[i] * kernel[i]; + + FragColor = vec4(col, 1.0); +} +</code></pre> + +<p> + In the fragment shader we first create an array of 9 <code>vec2</code> offsets for each surrounding texture coordinate. The offset is a constant value that you could customize to your liking. Then we define the kernel, which in this case is a <def>sharpen</def> kernel that sharpens each color value by sampling all surrounding pixels in an interesting way. Lastly, we add each offset to the current texture coordinate when sampling and multiply these texture values with the weighted kernel values that we add together. +</p> + +<p> + This particular sharpen kernel looks like this: +</p> + +<img src="/img/advanced/framebuffers_sharpen.png" class="clean" alt="Post-processing image of a 3D scene in OpenGL with blurred colors"/> + +<p> + This could be the base of some interesting effects where your player may be on a narcotic adventure. +</p> + + +<h3>Blur</h3> +<p> + A kernel that creates a <def>blur</def> effect is defined as follows: +</p> + +\[\begin{bmatrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{bmatrix} / 16\] + +<p> + Because all values add up to 16, directly returning the combined sampled colors would result in an extremely bright color so we have to divide each value of the kernel by <code>16</code>. The resulting kernel array then becomes: +</p> + +<pre><code> +float kernel[9] = float[]( + 1.0 / 16, 2.0 / 16, 1.0 / 16, + 2.0 / 16, 4.0 / 16, 2.0 / 16, + 1.0 / 16, 2.0 / 16, 1.0 / 16 +); +</code></pre> + +<p> + By only changing the kernel array in the fragment shader we can completely change the post-processing effect. It now looks something like this: +</p> + +<img src="/img/advanced/framebuffers_blur.png" class="clean" alt="Post-processing image of a 3D scene in OpenGL with sharpened colors"/> + + +<p> + Such a blur effect creates interesting possibilities. We could vary the blur amount over time to create the effect of someone being drunk, or increase the blur whenever the main character is not wearing glasses. Blurring can also be a useful tool for smoothing color values which we'll see use of in later chapters. +</p> + +<p> + You can see that once we have such a little kernel implementation in place it is quite easy to create cool post-processing effects. Let's show you a last popular effect to finish this discussion. +</p> + +<h3>Edge detection</h3> +<p> + Below you can find an <def>edge-detection</def> kernel that is similar to the sharpen kernel: +</p> + +\[\begin{bmatrix} 1 & 1 & 1 \\ 1 & -8 & 1 \\ 1 & 1 & 1 \end{bmatrix}\] + +<p> + This kernel highlights all edges and darkens the rest, which is pretty useful when we only care about edges in an image. +</p> + +<img src="/img/advanced/framebuffers_edge_detection.png" class="clean" alt="Post-processing image of a 3D scene in OpenGL with edge detection filter"/> + +<p> + It probably does not come as a surprise that kernels like this are used as image-manipulating tools/filters in tools like Photoshop. Because of a graphic card's ability to process fragments with extreme parallel capabilities, we can manipulate images on a per-pixel basis in real-time with relative ease. Image-editing tools therefore tend to use graphics cards for image-processing. +</p> + + +<h2>Exercises</h2> +<ul> + <li>Can you use framebuffers to create a rear-view mirror? For this you'll have to draw your scene twice: one with the camera rotated 180 degrees and the other as normal. Try to create a small quad at the top of your screen to apply the mirror texture on, something like <a href="/img/advanced/framebuffers_mirror.png" target="_blank">this</a>; <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/5.2.framebuffers_exercise1/framebuffers_exercise1.cpp" target="_blank">solution</a>.</li> + <li>Play around with the kernel values and create your own interesting post-processing effects. Try searching the internet as well for other interesting kernels.</li> +</ul> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Geometry-Shader.html b/pub/Advanced-OpenGL/Geometry-Shader.html @@ -0,0 +1,948 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Geometry Shader</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Geometry-Shader</h1> +<p> + Between the vertex and the fragment shader there is an optional shader stage called the <def>geometry shader</def>. A geometry shader takes as input a set of vertices that form a single primitive e.g. a point or a triangle. The geometry shader can then transform these vertices as it sees fit before sending them to the next shader stage. What makes the geometry shader interesting is that it is able to convert the original primitive (set of vertices) to completely different primitives, possibly generating more vertices than were initially given. +</p> + +<p> + We're going to throw you right into the deep by showing you an example of a geometry shader: +</p> + +<pre><code> +#version 330 core +layout (points) in; +layout (line_strip, max_vertices = 2) out; + +void main() { + gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); + EmitVertex(); + + gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0); + EmitVertex(); + + EndPrimitive(); +} +</code></pre> + +<p> + At the start of a geometry shader we need to declare the type of primitive input we're receiving from the vertex shader. We do this by declaring a layout specifier in front of the <fun>in</fun> keyword. This input layout qualifier can take any of the following primitive values: +</p> + +<ul> + <li><code>points</code>: when drawing <var>GL_POINTS</var> primitives (<code>1</code>).</li> + <li><code>lines</code>: when drawing <var>GL_LINES</var> or <var>GL_LINE_STRIP</var> (<code>2</code>).</li> + <li><code>lines_adjacency</code>: <var>GL_LINES_ADJACENCY</var> or <var>GL_LINE_STRIP_ADJACENCY</var> (<code>4</code>).</li> + <li><code>triangles</code>: <var>GL_TRIANGLES</var>, <var>GL_TRIANGLE_STRIP</var> or <var>GL_TRIANGLE_FAN</var> (<code>3</code>).</li> + <li><code>triangles_adjacency </code>: <var>GL_TRIANGLES_ADJACENCY</var> or <var>GL_TRIANGLE_STRIP_ADJACENCY </var> (<code>6</code>).</li> +</ul> + +<p> + These are almost all the rendering primitives we're able to give to rendering calls like <fun><function id='1'>glDrawArrays</function></fun>. If we'd chosen to draw vertices as <var>GL_TRIANGLES</var> we should set the input qualifier to <code>triangles</code>. The number within the parenthesis represents the minimal number of vertices a single primitive contains. +</p> + +<p> + We also need to specify a primitive type that the geometry shader will output and we do this via a layout specifier in front of the <fun>out</fun> keyword. Like the input layout qualifier, the output layout qualifier can take several primitive values: +</p> + +<ul> + <li><code>points</code></li> + <li><code>line_strip</code></li> + <li><code>triangle_strip</code></li> +</ul> + +<p> + With just these 3 output specifiers we can create almost any shape we want from the input primitives. To generate a single triangle for example we'd specify <code>triangle_strip</code> as the output and output 3 vertices. +</p> + +<p> + The geometry shader also expects us to set a maximum number of vertices it outputs (if you exceed this number, OpenGL won't draw the <em>extra</em> vertices) which we can also do within the layout qualifier of the <fun>out</fun> keyword. In this particular case we're going to output a <code>line_strip</code> with a maximum number of 2 vertices. +</p> + +<note> + In case you're wondering what a line strip is: a line strip binds together a set of points to form one continuous line between them with a minimum of 2 points. Each extra point results in a new line between the new point and the previous point as you can see in the following image with 5 point vertices: + +<img src="/img/advanced/geometry_shader_line_strip.png" class="clean" alt="Image of line_strip primitive in geometry shader"/> +</note> + +<p> + To generate meaningful results we need some way to retrieve the output from the previous shader stage. GLSL gives us a <def>built-in</def> variable called <fun>gl_in</fun> that internally (probably) looks something like this: +</p> + +<pre><code> +in gl_Vertex +{ + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[]; +} gl_in[]; +</code></pre> + +<p> + Here it is declared as an <def>interface block</def> (as discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL" target="_blank">previous</a> chapter) that contains a few interesting variables of which the most interesting one is <var>gl_Position</var> that contains the vector we set as the vertex shader's output. +</p> + +<p> + Note that it is declared as an array, because most render primitives contain more than 1 vertex. The geometry shader receives <strong>all</strong> vertices of a primitive as its input. +</p> + +<p> + Using the vertex data from the vertex shader stage we can generate new data with 2 geometry shader functions called <fun>EmitVertex</fun> and <fun>EndPrimitive</fun>. The geometry shader expects you to generate/output at least one of the primitives you specified as output. In our case we want to at least generate one line strip primitive. +</p> + +<pre><code> +#version 330 core +layout (points) in; +layout (line_strip, max_vertices = 2) out; + +void main() { + gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); + EmitVertex(); + + gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0); + EmitVertex(); + + EndPrimitive(); +} +</code></pre> + +<p> + Each time we call <fun>EmitVertex</fun>, the vector currently set to <var>gl_Position</var> is added to the output primitive. Whenever <fun>EndPrimitive</fun> is called, all emitted vertices for this primitive are combined into the specified output render primitive. By repeatedly calling <fun>EndPrimitive</fun>, after one or more <fun>EmitVertex</fun> calls, multiple primitives can be generated. This particular case emits two vertices that were translated by a small offset from the original vertex position and then calls <fun>EndPrimitive</fun>, combining the two vertices into a single line strip of 2 vertices. +</p> + +<p> + Now that you (sort of) know how geometry shaders work you can probably guess what this geometry shader does. This geometry shader takes a point primitive as its input and creates a horizontal line primitive with the input point at its center. If we were to render this it looks something like this: +</p> + +<img src="/img/advanced/geometry_shader_lines.png" class="clean" alt="Geometry shader drawing lines out of points in OpenGL"/> + +<p> + Not very impressive yet, but it's interesting to consider that this output was generated using just the following render call: +</p> + +<pre class="cpp"><code> +<function id='1'>glDrawArrays</function>(GL_POINTS, 0, 4); +</code></pre> + +<p> + While this is a relatively simple example, it does show you how we can use geometry shaders to (dynamically) generate new shapes on the fly. Later in this chapter we'll discuss a few interesting effects that we can create using geometry shaders, but for now we're going to start with a simple example. +</p> + +<h2>Using geometry shaders</h2> +<p> + To demonstrate the use of a geometry shader we're going to render a really simple scene where we draw 4 points on the z-plane in normalized device coordinates. The coordinates of the points are: +</p> + +<pre><code> +float points[] = { + -0.5f, 0.5f, // top-left + 0.5f, 0.5f, // top-right + 0.5f, -0.5f, // bottom-right + -0.5f, -0.5f // bottom-left +}; +</code></pre> + +<p> + The vertex shader needs to draw the points on the z-plane so we'll create a basic vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec2 aPos; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); +} +</code></pre> + +<p> + And we'll output the color green for all points which we code directly in the fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(0.0, 1.0, 0.0, 1.0); +} +</code></pre> + +<p> + Generate a VAO and a VBO for the points' vertex data and then draw them via <fun><function id='1'>glDrawArrays</function></fun>: +</p> + +<pre class="cpp"><code> +shader.use(); +<function id='27'>glBindVertexArray</function>(VAO); +<function id='1'>glDrawArrays</function>(GL_POINTS, 0, 4); +</code></pre> + +<p> + The result is a dark scene with 4 (difficult to see) green points: +</p> + +<img src="/img/advanced/geometry_shader_points.png" class="clean" alt="4 Points drawn using OpenGL"/> + +<p> + But didn't we already learn to do all this? Yes, and now we're going to spice this little scene up by adding geometry shader magic to the scene. +</p> + +<p> + For learning purposes we're first going to create what is called a <def>pass-through</def> geometry shader that takes a point primitive as its input and <em>passes</em> it to the next shader unmodified: +</p> + +<pre><code> +#version 330 core +layout (points) in; +layout (points, max_vertices = 1) out; + +void main() { + gl_Position = gl_in[0].gl_Position; + EmitVertex(); + EndPrimitive(); +} +</code></pre> + +<p> + By now this geometry shader should be fairly easy to understand. It simply emits the unmodified vertex position it received as input and generates a point primitive. +</p> + +<p> + A geometry shader needs to be compiled and linked to a program just like the vertex and fragment shader, but this time we'll create the shader using <var>GL_GEOMETRY_SHADER</var> as the shader type: +</p> + +<pre class="cpp"><code> +geometryShader = <function id='37'>glCreateShader</function>(GL_GEOMETRY_SHADER); +<function id='42'>glShaderSource</function>(geometryShader, 1, &gShaderCode, NULL); +<function id='38'>glCompileShader</function>(geometryShader); +[...] +<function id='34'>glAttachShader</function>(program, geometryShader); +<function id='35'>glLinkProgram</function>(program); +</code></pre> + +<p> + The shader compilation code is the same as the vertex and fragment shaders. Be sure to check for compile or linking errors! +</p> + +<p> + If you'd now compile and run you should be looking at a result that looks a bit like this: +</p> + +<img src="/img/advanced/geometry_shader_points.png" class="clean" alt="4 Points drawn using OpenGL (with geometry shader this time!)"/> + +<p> + It's exactly the same as without the geometry shader! It's a bit dull, I'll admit that, but the fact that we were still able to draw the points means that the geometry shader works, so now it's time for the more funky stuff! +</p> + +<h2>Let's build houses</h2> +<p> + Drawing points and lines isn't <strong>that</strong> interesting so we're going to get a little creative by using the geometry shader to draw a house for us at the location of each point. We can accomplish this by setting the output of the geometry shader to <def>triangle_strip</def> and draw a total of three triangles: two for the square house and one for the roof. +</p> + +<p> + A triangle strip in OpenGL is a more efficient way to draw triangles with fewer vertices. After the first triangle is drawn, each subsequent vertex generates another triangle next to the first triangle: every 3 adjacent vertices will form a triangle. If we have a total of 6 vertices that form a triangle strip we'd get the following triangles: (1,2,3), (2,3,4), (3,4,5) and (4,5,6); forming a total of 4 triangles. A triangle strip needs at least 3 vertices and will generate N-2 triangles; with 6 vertices we created 6-2 = 4 triangles. The following image illustrates this: +</p> + +<img src="/img/advanced/geometry_shader_triangle_strip.png" class="clean" alt="Image of a triangle strip with their index order in OpenGL"/> + +<p> + Using a triangle strip as the output of the geometry shader we can easily create the house shape we're after by generating 3 adjacent triangles in the correct order. The following image shows in what order we need to draw what vertices to get the triangles we need with the blue dot being the input point: +</p> + +<img src="/img/advanced/geometry_shader_house.png" class="clean" alt="How a house figure should be drawn from a single point using geometry shaders"/> + +<p> + This translates to the following geometry shader: +</p> + +<pre><code> +#version 330 core +layout (points) in; +layout (triangle_strip, max_vertices = 5) out; + +void build_house(vec4 position) +{ + gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:bottom-left + EmitVertex(); + gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right + EmitVertex(); + gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left + EmitVertex(); + gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right + EmitVertex(); + gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top + EmitVertex(); + EndPrimitive(); +} + +void main() { + build_house(gl_in[0].gl_Position); +} +</code></pre> + +<p> + This geometry shader generates 5 vertices, with each vertex being the point's position plus an offset to form one large triangle strip. The resulting primitive is then rasterized and the fragment shader runs on the entire triangle strip, resulting in a green house for each point we've rendered: +</p> + +<img src="/img/advanced/geometry_shader_houses.png" class="clean" alt="Houses drawn with points using geometry shader in OpenGL"/> + +<p> + You can see that each house indeed consists of 3 triangles - all drawn using a single point in space. The green houses do look a bit boring though, so let's liven it up a bit by giving each house a unique color. To do this we're going to add an extra vertex attribute in the vertex shader with color information per vertex and direct it to the geometry shader that further forwards it to the fragment shader. +</p> + +<p> + The updated vertex data is given below: +</p> + +<pre><code> +float points[] = { + -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // top-left + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // top-right + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // bottom-right + -0.5f, -0.5f, 1.0f, 1.0f, 0.0f // bottom-left +}; +</code></pre> + +<p> + Then we update the vertex shader to forward the color attribute to the geometry shader using an interface block: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec3 aColor; + +out VS_OUT { + vec3 color; +} vs_out; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); + vs_out.color = aColor; +} +</code></pre> + +<p> + Then we also need to declare the same interface block (with a different interface name) in the geometry shader: +</p> + +<pre><code> +in VS_OUT { + vec3 color; +} gs_in[]; +</code></pre> + +<p> + Because the geometry shader acts on a set of vertices as its input, its input data from the vertex shader is always represented as arrays of vertex data even though we only have a single vertex right now. +</p> + +<note> + We don't necessarily have to use interface blocks to transfer data to the geometry shader. We could have also written it as: +<pre><code> +in vec3 outColor[]; +</code></pre> + This works if the vertex shader forwarded the color vector as <code>out</code> <code>vec3</code> <code>outColor</code>. However, interface blocks are easier to work with in shaders like the geometry shader. In practice, geometry shader inputs can get quite large and grouping them in one large interface block array makes a lot more sense. +</note> + +<p> + We should also declare an output color vector for the next fragment shader stage: +</p> + +<pre><code> +out vec3 fColor; +</code></pre> + +<p> + Because the fragment shader expects only a single (interpolated) color it doesn't make sense to forward multiple colors. The <var>fColor</var> vector is thus not an array, but a single vector. When emitting a vertex, that vertex will store the last stored value in <var>fColor</var> as that vertex's output value. For the houses, we can fill <var>fColor</var> once with the color from the vertex shader before the first vertex is emitted to color the entire house: +</p> + +<pre><code> +fColor = gs_in[0].color; // gs_in[0] since there's only one input vertex +gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:bottom-left +EmitVertex(); +gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right +EmitVertex(); +gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left +EmitVertex(); +gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right +EmitVertex(); +gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top +EmitVertex(); +EndPrimitive(); +</code></pre> + +<p> + All the emitted vertices will have the last stored value in <var>fColor</var> embedded into their data, which is equal to the input vertex's color as we defined in its attributes. All the houses will now have a color of their own: +</p> + +<img src="/img/advanced/geometry_shader_houses_colored.png" class="clean" alt="Colored houses, generating using points with geometry shaders in OpenGL"/> + +<p> + Just for fun we could also pretend it's winter and give their roofs a little snow by giving the last vertex a color of its own: +</p> + +<pre><code> +fColor = gs_in[0].color; +gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:bottom-left +EmitVertex(); +gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right +EmitVertex(); +gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left +EmitVertex(); +gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right +EmitVertex(); +gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top +fColor = vec3(1.0, 1.0, 1.0); +EmitVertex(); +EndPrimitive(); +</code></pre> + +<p> + The result now looks something like this: +</p> + +<img src="/img/advanced/geometry_shader_houses_snow.png" class="clean" alt="Snow-colored houses, generating using points with geometry shaders in OpenGL"/> + +<p> + You can compare your source code with the OpenGL code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/9.1.geometry_shader_houses/geometry_shader_houses.cpp" target="_blank">here</a>. +</p> + +<p> + You can see that with geometry shaders you can get pretty creative, even with the simplest primitives. Because the shapes are generated dynamically on the ultra-fast hardware of your GPU this can be a lot more powerful than defining these shapes yourself within vertex buffers. Geometry shaders are a great tool for simple (often-repeating) shapes, like cubes in a voxel world or grass leaves on a large outdoor field. +</p> + +<h1>Exploding objects</h1> +<p> + While drawing houses is fun and all, it's not something we're going to use that much. That's why we're now going to take it up one notch and explode objects! That is something we're also probably not going to use that much either, but it's definitely fun to do! +</p> + +<p> + When we say <em>exploding</em> an object we're not actually going to blow up our precious bundled sets of vertices, but we're going to move each triangle along the direction of their normal vector over a small period of time. The effect is that the entire object's triangles seem to <em>explode</em>. The effect of exploding triangles on the backpack model looks a bit like this: +</p> + +<img src="/img/advanced/geometry_shader_explosion.png" class="clean" alt="Explosion effect with geometry shaders in OpenGL"/> + +<p> + The great thing about such a geometry shader effect is that it works on all objects, regardless of their complexity. +</p> + +<p> + Because we're going to translate each vertex into the direction of the triangle's normal vector we first need to calculate this normal vector. What we need to do is calculate a vector that is perpendicular to the surface of a triangle, using just the 3 vertices we have access to. You may remember from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter that we can retrieve a vector perpendicular to two other vectors using the <def>cross product</def>. If we were to retrieve two vectors <var>a</var> and <var>b</var> that are parallel to the surface of a triangle we can retrieve its normal vector by doing a cross product on those vectors. The following geometry shader function does exactly this to retrieve the normal vector using 3 input vertex coordinates: +</p> + +<pre><code> +vec3 GetNormal() +{ + vec3 a = vec3(gl_in[0].gl_Position) - vec3(gl_in[1].gl_Position); + vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position); + return normalize(cross(a, b)); +} +</code></pre> + +<p> + Here we retrieve two vectors <var>a</var> and <var>b</var> that are parallel to the surface of the triangle using vector subtraction. Subtracting two vectors from each other results in a vector that is the difference of the two vectors. Since all 3 points lie on the triangle plane, subtracting any of its vectors from each other results in a vector parallel to the plane. Do note that if we switched <var>a</var> and <var>b</var> in the <fun>cross</fun> function we'd get a normal vector that points in the opposite direction - order is important here! +</p> + +<p> + Now that we know how to calculate a normal vector we can create an <fun>explode</fun> function that takes this normal vector along with a vertex position vector. The function returns a new vector that translates the position vector along the direction of the normal vector: +</p> + +<pre><code> +vec4 explode(vec4 position, vec3 normal) +{ + float magnitude = 2.0; + vec3 direction = normal * ((sin(time) + 1.0) / 2.0) * magnitude; + return position + vec4(direction, 0.0); +} +</code></pre> + +<p> + The function itself shouldn't be too complicated. The <fun>sin</fun> function receives a <var>time</var> uniform variable as its argument that, based on the time, returns a value between <code>-1.0</code> and <code>1.0</code>. Because we don't want to <em>implode</em> the object we transform the sin value to the <code>[0,1]</code> range. The resulting value is then used to scale the <var>normal</var> vector and the resulting <var>direction</var> vector is added to the position vector. +</p> + +<p> + The complete geometry shader for the <def>explode</def> effect, while drawing a model loaded using our <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">model loader</a>, looks a bit like this: +</p> + +<pre><code> +#version 330 core +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; + +in VS_OUT { + vec2 texCoords; +} gs_in[]; + +out vec2 TexCoords; + +uniform float time; + +vec4 explode(vec4 position, vec3 normal) { ... } + +vec3 GetNormal() { ... } + +void main() { + vec3 normal = GetNormal(); + + gl_Position = explode(gl_in[0].gl_Position, normal); + TexCoords = gs_in[0].texCoords; + EmitVertex(); + gl_Position = explode(gl_in[1].gl_Position, normal); + TexCoords = gs_in[1].texCoords; + EmitVertex(); + gl_Position = explode(gl_in[2].gl_Position, normal); + TexCoords = gs_in[2].texCoords; + EmitVertex(); + EndPrimitive(); +} +</code></pre> + +<p> + Note that we're also outputting the appropriate texture coordinates before emitting a vertex. +</p> + +<p> + Also don't forget to actually set the <var>time</var> uniform in your OpenGL code: +</p> + +<pre><code> +shader.setFloat("time", <function id='47'>glfwGetTime</function>()); +</code></pre> + +<p> + The result is a 3D model that seems to continually explode its vertices over time after which it returns to normal again. Although not exactly super useful, it does show you a more advanced use of the geometry shader. You can compare your source code with the complete source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/9.2.geometry_shader_exploding/geometry_shader_exploding.cpp" target="_blank">here</a>. +</p> + +<h1>Visualizing normal vectors</h1> +<p> + To shake things up we're going to now discuss an example of using the geometry shader that is actually useful: visualizing the normal vectors of any object. When programming lighting shaders you will eventually run into weird visual outputs of which the cause is hard to determine. A common cause of lighting errors is incorrect normal vectors. Either caused by incorrectly loading vertex data, improperly specifying them as vertex attributes, or by incorrectly managing them in the shaders. What we want is some way to detect if the normal vectors we supplied are correct. A great way to determine if your normal vectors are correct is by visualizing them, and it just so happens that the geometry shader is an extremely useful tool for this purpose. +</p> + +<p> + The idea is as follows: we first draw the scene as normal without a geometry shader and then we draw the scene a second time, but this time only displaying normal vectors that we generate via a geometry shader. The geometry shader takes as input a triangle primitive and generates 3 lines from them in the directions of their normal - one normal vector for each vertex. In code it'll look something like this: +</p> + +<pre><code> +shader.use(); +DrawScene(); +normalDisplayShader.use(); +DrawScene(); +</code></pre> + +<p> + This time we're creating a geometry shader that uses the vertex normals supplied by the model instead of generating it ourself. To accommodate for scaling and rotations (due to the view and model matrix) we'll transform the normals with a normal matrix. The geometry shader receives its position vectors as view-space coordinates so we should also transform the normal vectors to the same space. This can all be done in the vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +out VS_OUT { + vec3 normal; +} vs_out; + +uniform mat4 view; +uniform mat4 model; + +void main() +{ + gl_Position = view * model * vec4(aPos, 1.0); + mat3 normalMatrix = mat3(transpose(inverse(view * model))); + vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0))); +} +</code></pre> + +<p> + The transformed view-space normal vector is then passed to the next shader stage via an interface block. The geometry shader then takes each vertex (with a position and a normal vector) and draws a normal vector from each position vector: +</p> + +<pre><code> +#version 330 core +layout (triangles) in; +layout (line_strip, max_vertices = 6) out; + +in VS_OUT { + vec3 normal; +} gs_in[]; + +const float MAGNITUDE = 0.4; + +uniform mat4 projection; + +void GenerateLine(int index) +{ + gl_Position = projection * gl_in[index].gl_Position; + EmitVertex(); + gl_Position = projection * (gl_in[index].gl_Position + + vec4(gs_in[index].normal, 0.0) * MAGNITUDE); + EmitVertex(); + EndPrimitive(); +} + +void main() +{ + GenerateLine(0); // first vertex normal + GenerateLine(1); // second vertex normal + GenerateLine(2); // third vertex normal +} +</code></pre> + +<p> + The contents of geometry shaders like these should be self-explanatory by now. Note that we're multiplying the normal vector by a <var>MAGNITUDE</var> vector to restrain the size of the displayed normal vectors (otherwise they'd be a bit too large). +</p> + +<p> + Since visualizing normals are mostly used for debugging purposes we can just display them as mono-colored lines (or super-fancy lines if you feel like it) with the help of the fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0, 1.0, 0.0, 1.0); +} +</code></pre> + +<p> + Now rendering your model with normal shaders first and then with the special <em>normal-visualizing</em> shader you'll see something like this: +</p> + +<img src="/img/advanced/geometry_shader_normals.png" class="clean" alt="Image of geometry shader displaying normal vectors in OpenGL"/> + +<p> + Apart from the fact that our backpack now looks a bit hairy, it gives us a really useful method for determining if the normal vectors of a model are indeed correct. You can imagine that geometry shaders like this could also be used for adding <def>fur</def> to objects. +</p> + +<p> + You can find the OpenGL's source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/9.3.geometry_shader_normals/normal_visualization.cpp" target="_blank">here</a>. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Instancing.html b/pub/Advanced-OpenGL/Instancing.html @@ -0,0 +1,763 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Instancing</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Instancing</h1> +<p> + Say you have a scene where you're drawing a lot of models where most of these models contain the same set of vertex data, but with different world transformations. Think of a scene filled with grass leaves: each grass leave is a small model that consists of only a few triangles. You'll probably want to draw quite a few of them and your scene may end up with thousands or maybe tens of thousands of grass leaves that you need to render each frame. Because each leaf is only a few triangles, the leaf is rendered almost instantly. However, the thousands of render calls you'll have to make will drastically reduce performance. +</p> + +<p> + If we were to actually render such a large amount of objects it will look a bit like this in code: +</p> + +<pre><code> +for(unsigned int i = 0; i &lt; amount_of_models_to_draw; i++) +{ + DoSomePreparations(); // bind VAO, bind textures, set uniforms etc. + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, amount_of_vertices); +} +</code></pre> + +<p> + When drawing many <def>instances</def> of your model like this you'll quickly reach a performance bottleneck because of the many draw calls. Compared to rendering the actual vertices, telling the GPU to render your vertex data with functions like <fun><function id='1'>glDrawArrays</function></fun> or <fun><function id='2'>glDrawElements</function></fun> eats up quite some performance since OpenGL must make necessary preparations before it can draw your vertex data (like telling the GPU which buffer to read data from, where to find vertex attributes and all this over the relatively slow CPU to GPU bus). So even though rendering your vertices is super fast, giving your GPU the commands to render them isn't. +</p> + +<p> + It would be much more convenient if we could send data over to the GPU once, and then tell OpenGL to draw multiple objects using this data with a single drawing call. Enter <def>instancing</def>. +</p> + +<p> + Instancing is a technique where we draw many (equal mesh data) objects at once with a single render call, saving us all the CPU -> GPU communications each time we need to render an object. To render using instancing all we need to do is change the render calls <fun><function id='1'>glDrawArrays</function></fun> and <fun><function id='2'>glDrawElements</function></fun> to <fun><function id='98'><function id='1'>glDrawArrays</function>Instanced</function></fun> and <fun><function id='99'><function id='2'>glDrawElements</function>Instanced</function></fun> respectively. These <em>instanced</em> versions of the classic rendering functions take an extra parameter called the <def>instance count</def> that sets the number of instances we want to render. We sent all the required data to the GPU once, and then tell the GPU how it should draw all these instances with a single call. The GPU then renders all these instances without having to continually communicate with the CPU. +</p> + +<p> + By itself this function is a bit useless. Rendering the same object a thousand times is of no use to us since each of the rendered objects is rendered exactly the same and thus also at the same location; we would only see one object! For this reason GLSL added another built-in variable in the vertex shader called <var>gl_InstanceID</var>. +</p> + +<p> + When drawing with one of the instanced rendering calls, <var>gl_InstanceID</var> is incremented for each instance being rendered starting from <code>0</code>. If we were to render the 43th instance for example, <var>gl_InstanceID</var> would have the value <code>42</code> in the vertex shader. Having a unique value per instance means we could now for example index into a large array of position values to position each instance at a different location in the world. +</p> + +<p> + To get a feel for instanced drawing we're going to demonstrate a simple example that renders a hundred 2D quads in normalized device coordinates with just one render call. We accomplish this by uniquely positioning each instanced quad by indexing a uniform array of <code>100</code> offset vectors. The result is a neatly organized grid of quads that fill the entire window: +</p> + +<img src="/img/advanced/instancing_quads.png" class="clean" alt="100 Quads drawn via OpenGL instancing."/> + +<p> + Each quad consists of 2 triangles with a total of 6 vertices. Each vertex contains a 2D NDC position vector and a color vector. Below is the vertex data used for this example - the triangles are small enough to properly fit the screen when there's a 100 of them: +</p> + +<pre><code> +float quadVertices[] = { + // positions // colors + -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, + 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, + -0.05f, -0.05f, 0.0f, 0.0f, 1.0f, + + -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, + 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, + 0.05f, 0.05f, 0.0f, 1.0f, 1.0f +}; +</code></pre> + +<p> + The quads are colored in the fragment shader that receives a color vector from the vertex shader and sets it as its output: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec3 fColor; + +void main() +{ + FragColor = vec4(fColor, 1.0); +} +</code></pre> + +<p> + Nothing new so far, but at the vertex shader it's starting to get interesting: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec3 aColor; + +out vec3 fColor; + +uniform vec2 offsets[100]; + +void main() +{ + vec2 offset = offsets[gl_InstanceID]; + gl_Position = vec4(aPos + offset, 0.0, 1.0); + fColor = aColor; +} +</code></pre> + +<p> + Here we defined a uniform array called <var>offsets</var> that contain a total of <code>100</code> offset vectors. Within the vertex shader we retrieve an offset vector for each instance by indexing the <var>offsets</var> array using <var>gl_InstanceID</var>. If we now were to draw <code>100</code> quads with instanced drawing we'd get <code>100</code> quads located at different positions. +</p> + +<p> + We do need to actually set the offset positions that we calculate in a nested for-loop before we enter the render loop: +</p> + +<pre><code> +glm::vec2 translations[100]; +int index = 0; +float offset = 0.1f; +for(int y = -10; y &lt; 10; y += 2) +{ + for(int x = -10; x &lt; 10; x += 2) + { + glm::vec2 translation; + translation.x = (float)x / 10.0f + offset; + translation.y = (float)y / 10.0f + offset; + translations[index++] = translation; + } +} +</code></pre> + +<p> + Here we create a set of <code>100</code> translation vectors that contains an offset vector for all positions in a 10x10 grid. In addition to generating the <var>translations</var> array, we'd also need to transfer the data to the vertex shader's uniform array: +</p> + +<pre><code> +shader.use(); +for(unsigned int i = 0; i &lt; 100; i++) +{ + shader.setVec2(("offsets[" + std::to_string(i) + "]")), translations[i]); +} +</code></pre> + +<p> + Within this snippet of code we transform the for-loop counter <var>i</var> to a <fun>string</fun> to dynamically create a location string for querying the uniform location. For each item in the <var>offsets</var> uniform array we then set the corresponding translation vector. +</p> + +<p> + Now that all the preparations are finished we can start rendering the quads. To draw via instanced rendering we call <fun><function id='98'><function id='1'>glDrawArrays</function>Instanced</function></fun> or <fun><function id='99'><function id='2'>glDrawElements</function>Instanced</function></fun>. Since we're not using an element index buffer we're going to call the <fun><function id='1'>glDrawArrays</function></fun> version: +</p> + +<pre class="cpp"><code> +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='98'><function id='1'>glDrawArrays</function>Instanced</function>(GL_TRIANGLES, 0, 6, 100); +</code></pre> + +<p> + The parameters of <fun><function id='98'><function id='1'>glDrawArrays</function>Instanced</function></fun> are exactly the same as <fun><function id='1'>glDrawArrays</function></fun> except the last parameter that sets the number of instances we want to draw. Since we want to display <code>100</code> quads in a 10x10 grid we set it equal to <code>100</code>. Running the code should now give you the familiar image of <code>100</code> colorful quads. +</p> + +<h2>Instanced arrays</h2> +<p> + While the previous implementation works fine for this specific use case, whenever we are rendering a lot more than <code>100</code> instances (which is quite common) we will eventually hit a <a href="https://www.khronos.org/opengl/wiki/GLSL_Uniform#Implementation_limits" target="_blank">limit</a> on the amount of uniform data we can send to the shaders. One alternative option is known as <def>instanced arrays</def>. Instanced arrays are defined as a vertex attribute (allowing us to store much more data) that are updated per instance instead of per vertex. +</p> + +<p> + With vertex attributes, at the start of each run of the vertex shader, the GPU will retrieve the next set of vertex attributes that belong to the current vertex. When defining a vertex attribute as an instanced array however, the vertex shader only updates the content of the vertex attribute per instance. This allows us to use the standard vertex attributes for data per vertex and use the instanced array for storing data that is unique per instance. +</p> + +<p> + To give you an example of an instanced array we're going to take the previous example and convert the offset uniform array to an instanced array. We'll have to update the vertex shader by adding another vertex attribute: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aOffset; + +out vec3 fColor; + +void main() +{ + gl_Position = vec4(aPos + aOffset, 0.0, 1.0); + fColor = aColor; +} +</code></pre> + +<p> + We no longer use <var>gl_InstanceID</var> and can directly use the <var>offset</var> attribute without first indexing into a large uniform array. +</p> + +<p> + Because an instanced array is a vertex attribute, just like the <var>position</var> and <var>color</var> variables, we need to store its content in a vertex buffer object and configure its attribute pointer. We're first going to store the <var>translations</var> array (from the previous section) in a new buffer object: +</p> + +<pre><code> +unsigned int instanceVBO; +<function id='12'>glGenBuffers</function>(1, &instanceVBO); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, instanceVBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &translations[0], GL_STATIC_DRAW); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0); +</code></pre> + +<p> + Then we also need to set its vertex attribute pointer and enable the vertex attribute: +</p> + +<pre><code> +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(2); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, instanceVBO); +<function id='30'>glVertexAttribPointer</function>(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0); +<function id='100'>glVertexAttribDivisor</function>(2, 1); +</code></pre> + +<p> + What makes this code interesting is the last line where we call <fun><function id='100'>glVertexAttribDivisor</function></fun>. This function tells OpenGL <strong>when</strong> to update the content of a vertex attribute to the next element. Its first parameter is the vertex attribute in question and the second parameter the <def>attribute divisor</def>. By default, the attribute divisor is <code>0</code> which tells OpenGL to update the content of the vertex attribute each iteration of the vertex shader. By setting this attribute to <code>1</code> we're telling OpenGL that we want to update the content of the vertex attribute when we start to render a new instance. By setting it to <code>2</code> we'd update the content every 2 instances and so on. By setting the attribute divisor to <code>1</code> we're effectively telling OpenGL that the vertex attribute at attribute location <code>2</code> is an instanced array. +</p> + +<p> + If we now were to render the quads again with <fun><function id='98'><function id='1'>glDrawArrays</function>Instanced</function></fun> we'd get the following output: +</p> + +<img src="/img/advanced/instancing_quads.png" class="clean" alt="Same image of OpenGL instanced quads, but this time using instanced arrays."/> + +<p> + This is exactly the same as the previous example, but now with instanced arrays, which allows us to pass a lot more data (as much as memory allows us) to the vertex shader for instanced drawing. +</p> + +<p> + For fun we could slowly downscale each quad from top-right to bottom-left using <var>gl_InstanceID</var> again, because why not? +</p> + +<pre><code> +void main() +{ + vec2 pos = aPos * (gl_InstanceID / 100.0); + gl_Position = vec4(pos + aOffset, 0.0, 1.0); + fColor = aColor; +} +</code></pre> + +<p> + The result is that the first instances of the quads are drawn extremely small and the further we're in the process of drawing the instances, the closer <var>gl_InstanceID</var> gets to <code>100</code> and thus the more the quads regain their original size. It's perfectly legal to use instanced arrays together with <var>gl_InstanceID</var> like this. +</p> + +<img src="/img/advanced/instancing_quads_arrays.png" class="clean" alt="Image of instanced quads drawn in OpenGL using instanced arrays"/> + +<p> + If you're still a bit unsure about how instanced rendering works or want to see how everything fits together you can find the full source code of the application <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/10.1.instancing_quads/instancing_quads.cpp" target="_blank">here</a>. +</p> + +<p> + While fun and all, these examples aren't really good examples of instancing. Yes, they do give you an easy overview of how instancing works, but instancing gets most of its power when drawing an enormous amount of similar objects. For that reason we're going to venture into space. +</p> + +<h1>An asteroid field</h1> +<p> + Imagine a scene where we have one large planet that's at the center of a large asteroid ring. Such an asteroid ring could contain thousands or tens of thousands of rock formations and quickly becomes un-renderable on any decent graphics card. This scenario proves itself particularly useful for instanced rendering, since all the asteroids can be represented with a single model. Each single asteroid then gets its variation from a transformation matrix unique to each asteroid. +</p> + +<p> + To demonstrate the impact of instanced rendering we're first going to render a scene of asteroids hovering around a planet <em>without</em> instanced rendering. The scene will contain a large planet model that can be downloaded from <a href="/data/models/planet.zip" target="_blank">here</a> and a large set of asteroid rocks that we properly position around the planet. The asteroid rock model can be downloaded <a href="/data/models/rock.zip" target="_blank">here</a>. +</p> + +<p> + Within the code samples we load the models using the model loader we've previously defined in the <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">model loading</a> chapters. +</p> + +<p> + To achieve the effect we're looking for we'll be generating a model transformation matrix for each asteroid. The transformation matrix first translates the rock somewhere in the asteroid ring - then we'll add a small random displacement value to the offset to make the ring look more natural. From there we also apply a random scale and a random rotation. The result is a transformation matrix that translates each asteroid somewhere around the planet while also giving it a more natural and unique look compared to the other asteroids. +</p> + +<pre><code> +unsigned int amount = 1000; +glm::mat4 *modelMatrices; +modelMatrices = new glm::mat4[amount]; +srand(<function id='47'>glfwGetTime</function>()); // initialize random seed +float radius = 50.0; +float offset = 2.5f; +for(unsigned int i = 0; i &lt; amount; i++) +{ + glm::mat4 model = glm::mat4(1.0f); + // 1. translation: displace along circle with 'radius' in range [-offset, offset] + float angle = (float)i / (float)amount * 360.0f; + float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset; + float x = sin(angle) * radius + displacement; + displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset; + float y = displacement * 0.4f; // keep height of field smaller compared to width of x and z + displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset; + float z = cos(angle) * radius + displacement; + model = <function id='55'>glm::translate</function>(model, glm::vec3(x, y, z)); + + // 2. scale: scale between 0.05 and 0.25f + float scale = (rand() % 20) / 100.0f + 0.05; + model = <function id='56'>glm::scale</function>(model, glm::vec3(scale)); + + // 3. rotation: add random rotation around a (semi)randomly picked rotation axis vector + float rotAngle = (rand() % 360); + model = <function id='57'>glm::rotate</function>(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f)); + + // 4. now add to list of matrices + modelMatrices[i] = model; +} +</code></pre> + +<p> + This piece of code may look a little daunting, but we basically transform the x and z position of the asteroid along a circle with a radius defined by <var>radius</var> and randomly displace each asteroid a little around the circle by <var>-offset</var> and <var>offset</var>. We give the <code>y</code> displacement less of an impact to create a more flat asteroid ring. Then we apply scale and rotation transformations and store the resulting transformation matrix in <var>modelMatrices</var> that is of size <var>amount</var>. Here we generate <code>1000</code> model matrices, one per asteroid. +</p> + +<p> + After loading the planet and rock models and compiling a set of shaders, the rendering code then looks a bit like this: +</p> + +<pre><code> +// draw planet +shader.use(); +glm::mat4 model = glm::mat4(1.0f); +model = <function id='55'>glm::translate</function>(model, glm::vec3(0.0f, -3.0f, 0.0f)); +model = <function id='56'>glm::scale</function>(model, glm::vec3(4.0f, 4.0f, 4.0f)); +shader.setMat4("model", model); +planet.Draw(shader); + +// draw meteorites +for(unsigned int i = 0; i &lt; amount; i++) +{ + shader.setMat4("model", modelMatrices[i]); + rock.Draw(shader); +} +</code></pre> + +<p> + First we draw the planet model, that we translate and scale a bit to accommodate the scene, and then we draw a number of rock models equal to the <var>amount</var> of transformations we generated previously. Before we draw each rock however, we first set the corresponding model transformation matrix within the shader. +</p> + +<p> + The result is then a space-like scene where we can see a natural-looking asteroid ring around a planet: +</p> + +<img src="/img/advanced/instancing_asteroids.png" class="clean" alt="Image of asteroid field drawn in OpenGL"/> + +<p> + This scene contains a total of <code>1001</code> rendering calls per frame of which <code>1000</code> are of the rock model. You can find the source code for this scene <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/10.2.asteroids/asteroids.cpp" target="_blank">here</a>. +</p> + +<p> + As soon as we start to increase this number we will quickly notice that the scene stops running smoothly and the number of frames we're able to render per second reduces drastically. As soon as we set <var>amount</var> to something close to <code>2000</code> the scene already becomes so slow on our GPU that it becomes difficult to move around. +</p> + +<p> + Let's now try to render the same scene, but this time with instanced rendering. We first need to adjust the vertex shader a little: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in mat4 instanceMatrix; + +out vec2 TexCoords; + +uniform mat4 projection; +uniform mat4 view; + +void main() +{ + gl_Position = projection * view * instanceMatrix * vec4(aPos, 1.0); + TexCoords = aTexCoords; +} +</code></pre> + +<p> + We're no longer using a model uniform variable, but instead declare a <fun>mat4</fun> as a vertex attribute so we can store an instanced array of transformation matrices. However, when we declare a datatype as a vertex attribute that is greater than a <fun>vec4</fun> things work a bit differently. The maximum amount of data allowed for a vertex attribute is equal to a <fun>vec4</fun>. Because a <fun>mat4</fun> is basically 4 <fun>vec4</fun>s, we have to reserve 4 vertex attributes for this specific matrix. Because we assigned it a location of <code>3</code>, the columns of the matrix will have vertex attribute locations of <code>3</code>, <code>4</code>, <code>5</code>, and <code>6</code>. +</p> + +<p> + We then have to set each of the attribute pointers of those <code>4</code> vertex attributes and configure them as instanced arrays: +</p> + +<pre><code> +// vertex buffer object +unsigned int buffer; +<function id='12'>glGenBuffers</function>(1, &buffer); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, buffer); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), &modelMatrices[0], GL_STATIC_DRAW); + +for(unsigned int i = 0; i &lt; rock.meshes.size(); i++) +{ + unsigned int VAO = rock.meshes[i].VAO; + <function id='27'>glBindVertexArray</function>(VAO); + // vertex attributes + std::size_t vec4Size = sizeof(glm::vec4); + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(3); + <function id='30'>glVertexAttribPointer</function>(3, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)0); + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(4); + <function id='30'>glVertexAttribPointer</function>(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(1 * vec4Size)); + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(5); + <function id='30'>glVertexAttribPointer</function>(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(2 * vec4Size)); + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(6); + <function id='30'>glVertexAttribPointer</function>(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(3 * vec4Size)); + + <function id='100'>glVertexAttribDivisor</function>(3, 1); + <function id='100'>glVertexAttribDivisor</function>(4, 1); + <function id='100'>glVertexAttribDivisor</function>(5, 1); + <function id='100'>glVertexAttribDivisor</function>(6, 1); + + <function id='27'>glBindVertexArray</function>(0); +} +</code></pre> + +<p> + Note that we cheated a little by declaring the <var>VAO</var> variable of the <fun>Mesh</fun> as a public variable instead of a private variable so we could access its vertex array object. This is not the cleanest solution, but just a simple modification to suit this example. Aside from the little hack, this code should be clear. We're basically declaring how OpenGL should interpret the buffer for each of the matrix's vertex attributes and that each of those vertex attributes is an instanced array. +</p> + +<p> + Next we take the <var>VAO</var> of the mesh(es) again and this time draw using <fun><function id='99'><function id='2'>glDrawElements</function>Instanced</function></fun>: +</p> + +<pre><code> +// draw meteorites +instanceShader.use(); +for(unsigned int i = 0; i &lt; rock.meshes.size(); i++) +{ + <function id='27'>glBindVertexArray</function>(rock.meshes[i].VAO); + <function id='99'><function id='2'>glDrawElements</function>Instanced</function>( + GL_TRIANGLES, rock.meshes[i].indices.size(), GL_UNSIGNED_INT, 0, amount + ); +} +</code></pre> + +<p> + Here we draw the same <var>amount</var> of asteroids as the previous example, but this time with instanced rendering. The results should be exactly the same, but once we increase the <var>amount</var> you'll really start to see the power of instanced rendering. Without instanced rendering we were able to smoothly render around <code>1000</code> to <code>1500</code> asteroids. With instanced rendering we can now set this value to <code>100000</code>. This, with the rock model having <code>576</code> vertices, would equal around <code>57</code> million vertices drawn each frame without significant performance drops; and only 2 draw calls! +</p> + +<img src="/img/advanced/instancing_asteroids_quantity.png" class="clean" alt="Image of asteroid field in OpenGL drawn using instanced rendering"/> + +<p> + This image was rendered with <code>100000</code> asteroids with a radius of <code>150.0f</code> and an offset equal to <code>25.0f</code>. You can find the source code of the instanced rendering demo <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/10.3.asteroids_instanced/asteroids_instanced.cpp" target="_blank">here</a>. +</p> + +<note> + On different machines an asteroid count of <code>100000</code> may be a bit too high, so try tweaking the values till you reach an acceptable framerate. +</note> + +<p> + As you can see, with the right type of environments, instanced rendering can make an enormous difference to the rendering capabilities of your application. For this reason, instanced rendering is commonly used for grass, flora, particles, and scenes like this - basically any scene with many repeating shapes can benefit from instanced rendering. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Advanced-OpenGL/Stencil-testing.html b/pub/Advanced-OpenGL/Stencil-testing.html @@ -0,0 +1,630 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Stencil testing</h1> +<h1 id="content-url" style='display:none;'>Advanced-OpenGL/Stencil-testing</h1> +<p> + Once the fragment shader has processed the fragment a so called <def>stencil test</def> is executed that, just like the depth test, has the option to discard fragments. After that the remaining fragments are passed to the depth test where OpenGL could possibly discard even more fragments. The stencil test is based on the content of yet another buffer called the <def>stencil buffer</def> that we're allowed to update during rendering to achieve interesting effects. +</p> + +<p> + A stencil buffer (usually) contains <code>8</code> bits per <def>stencil value</def> that amounts to a total of <code>256</code> different stencil values per pixel. We can set these stencil values to values of our liking and we can discard or keep fragments whenever a particular fragment has a certain stencil value. +</p> + +<note> + Each windowing library needs to set up a stencil buffer for you. GLFW does this automatically so we don't have to tell GLFW to create one, but other windowing libraries may not create a stencil buffer by default so be sure to check your library's documentation. +</note> + +<p> + A simple example of a stencil buffer is shown below (pixels not-to-scale): +</p> + +<img src="/img/advanced/stencil_buffer.png" class="clean" alt="A simple demonstration of a stencil buffer"/> + +<p> + The stencil buffer is first cleared with zeros and then an open rectangle of <code>1</code>s is stored in the stencil buffer. The fragments of the scene are then only rendered (the others are discarded) wherever the stencil value of that fragment contains a <code>1</code>. +</p> + +<p> + Stencil buffer operations allow us to set the stencil buffer at specific values wherever we're rendering fragments. By changing the content of the stencil buffer while we're rendering, we're <em>writing</em> to the stencil buffer. In the same (or following) frame(s) we can <em>read</em> these values to discard or pass certain fragments. When using stencil buffers you can get as crazy as you like, but the general outline is usually as follows: +</p> + +<ul> + <li>Enable writing to the stencil buffer.</li> + <li>Render objects, updating the content of the stencil buffer.</li> + <li>Disable writing to the stencil buffer.</li> + <li>Render (other) objects, this time discarding certain fragments based on the content of the stencil buffer.</li> +</ul> + +<p> + By using the stencil buffer we can thus discard certain fragments based on the fragments of other drawn objects in the scene. +</p> + +<p> + You can enable stencil testing by enabling <var>GL_STENCIL_TEST</var>. From that point on, all rendering calls will influence the stencil buffer in one way or another. +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_STENCIL_TEST); +</code></pre> + +<p> + Note that you also need to clear the stencil buffer each iteration just like the color and depth buffer: +</p> + +<pre><code> +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +</code></pre> + +<p> + Also, just like the depth testing's <fun><function id='65'>glDepthMask</function></fun> function, there is an equivalent function for the stencil buffer. The function <fun><function id='67'>glStencilMask</function></fun> allows us to set a bitmask that is <code>AND</code>ed with the stencil value about to be written to the buffer. By default this is set to a bitmask of all <code>1</code>s not affecting the output, but if we were to set this to <code>0x00</code> all the stencil values written to the buffer end up as <code>0</code>s. This is equivalent to depth testing's <fun><function id='65'>glDepthMask</function>(GL_FALSE)</fun>: +</p> + +<pre><code> +<function id='67'>glStencilMask</function>(0xFF); // each bit is written to the stencil buffer as is +<function id='67'>glStencilMask</function>(0x00); // each bit ends up as 0 in the stencil buffer (disabling writes) +</code></pre> + +<p> + Most of the cases you'll only be using <code>0x00</code> or <code>0xFF</code> as the stencil mask, but it's good to know there are options to set custom bit-masks. +</p> + +<h2>Stencil functions</h2> +<p> + Similar to depth testing, we have a certain amount of control over when a stencil test should pass or fail and how it should affect the stencil buffer. There are a total of two functions we can use to configure stencil testing: <fun><function id='68'>glStencilFunc</function></fun> and <fun><function id='69'>glStencilOp</function></fun>. +</p> + +<p> + The <fun><function id='68'>glStencilFunc</function>(GLenum func, GLint ref, GLuint mask)</fun> has three parameters: +</p> + +<ul> + <li><code>func</code>: sets the stencil test function that determines whether a fragment passes or is discarded. This test function is applied to the stored stencil value and the <fun><function id='68'>glStencilFunc</function></fun>'s <code>ref</code> value. Possible options are: <var>GL_NEVER</var>, <var>GL_LESS</var>, <var>GL_LEQUAL</var>, <var>GL_GREATER</var>, <var>GL_GEQUAL</var>, <var>GL_EQUAL</var>, <var>GL_NOTEQUAL</var> and <var>GL_ALWAYS</var>. The semantic meaning of these is similar to the depth buffer's functions.</li> + <li><code>ref</code>: specifies the reference value for the stencil test. The stencil buffer's content is compared to this value.</li> + <li><code>mask</code>: specifies a mask that is <code>AND</code>ed with both the reference value and the stored stencil value before the test compares them. Initially set to all <code>1</code>s.</li> +</ul> + +<p> + So in the case of the simple stencil example we've shown at the start, the function would be set to: +</p> + +<pre class="cpp"><code> +<function id='68'>glStencilFunc</function>(GL_EQUAL, 1, 0xFF) +</code></pre> + +<p> + This tells OpenGL that whenever the stencil value of a fragment is equal (<var>GL_EQUAL</var>) to the reference value <code>1</code>, the fragment passes the test and is drawn, otherwise discarded. +</p> + +<p> + But <fun><function id='68'>glStencilFunc</function></fun> only describes whether OpenGL should pass or discard fragments based on the stencil buffer's content, not how we can actually update the buffer. That is where <fun><function id='69'>glStencilOp</function></fun> comes in. +</p> + + <p> + The <fun><function id='69'>glStencilOp</function>(GLenum sfail, GLenum dpfail, GLenum dppass)</fun> contains three options of which we can specify for each option what action to take: + </p> + + <ul> + <li><code>sfail</code>: action to take if the stencil test fails.</li> + <li><code>dpfail</code>: action to take if the stencil test passes, but the depth test fails.</li> + <li><code>dppass</code>: action to take if both the stencil and the depth test pass.</li> + </ul> + +<p> + Then for each of the options you can take any of the following actions: +</p> + +<table> + <tr> + <th>Action</th> + <th>Description</th> + </tr> + <tr> + <td><code>GL_KEEP</code></td> + <td>The currently stored stencil value is kept.</td> + </tr> + <tr> + <td><code>GL_ZERO</code></td> + <td>The stencil value is set to <code>0</code>.</td> + </tr> + <tr> + <td><code>GL_REPLACE</code></td> + <td>The stencil value is replaced with the reference value set with <fun><function id='68'>glStencilFunc</function></fun>.</td> + </tr> + <tr> + <td><code>GL_INCR</code></td> + <td>The stencil value is increased by <code>1</code> if it is lower than the maximum value. </td> + </tr><tr> + <td><code>GL_INCR_WRAP</code></td> + <td>Same as <var>GL_INCR</var>, but wraps it back to <code>0</code> as soon as the maximum value is exceeded.</td> + </tr> + <tr> + <td><code>GL_DECR</code></td> + <td>The stencil value is decreased by <code>1</code> if it is higher than the minimum value.</td> + </tr> + <tr> + <td><code>GL_DECR_WRAP</code></td> + <td>Same as <var>GL_DECR</var>, but wraps it to the maximum value if it ends up lower than <code>0</code>.</td> + </tr> + <tr> + <td><code>GL_INVERT</code></td> + <td>Bitwise inverts the current stencil buffer value.</td> + </tr> +</table> + +<p> + By default the <fun><function id='69'>glStencilOp</function></fun> function is set to <code>(GL_KEEP, GL_KEEP, GL_KEEP)</code> so whatever the outcome of any of the tests, the stencil buffer keeps its values. The default behavior does not update the stencil buffer, so if you want to write to the stencil buffer you need to specify at least one different action for any of the options. +</p> + +<p> + So using <fun><function id='68'>glStencilFunc</function></fun> and <fun><function id='69'>glStencilOp</function></fun> we can precisely specify when and how we want to update the stencil buffer and when to pass or discard fragments based on its content. +</p> + +<h1>Object outlining</h1> +<p> + It would be unlikely if you completely understood how stencil testing works from the previous sections alone so we're going to demonstrate a particular useful feature that can be implemented with stencil testing alone called <def>object outlining</def>. +</p> + +<img src="/img/advanced/stencil_object_outlining.png" class="clean" alt="An object outlined using stencil testing/buffer"/> + +<p> + Object outlining does exactly what it says it does. For each object (or only one) we're creating a small colored border around the (combined) objects. This is a particular useful effect when you want to select units in a strategy game for example and need to show the user which of the units were selected. The routine for outlining your objects is as follows: +</p> + +<ol> + <li>Enable stencil writing.</li> + <li>Set the stencil op to <var>GL_ALWAYS</var> before drawing the (to be outlined) objects, updating the stencil buffer with <code>1</code>s wherever the objects' fragments are rendered.</li> + <li>Render the objects.</li> + <li>Disable stencil writing and depth testing.</li> + <li>Scale each of the objects by a small amount.</li> + <li>Use a different fragment shader that outputs a single (border) color.</li> + <li>Draw the objects again, but only if their fragments' stencil values are not equal to <code>1</code>.</li> + <li>Enable depth testing again and restore stencil func to <var>GL_KEEP</var>.</li> +</ol> + +<p> + This process sets the content of the stencil buffer to <code>1</code>s for each of the object's fragments and when it's time to draw the borders, we draw scaled-up versions of the objects only where the stencil test passes. We're effectively discarding all the fragments of the scaled-up versions that are part of the original objects' fragments using the stencil buffer. +</p> + +<p> + So we're first going to create a very basic fragment shader that outputs a border color. We simply set a hardcoded color value and call the shader <var>shaderSingleColor</var>: +</p> + +<pre><code> +void main() +{ + FragColor = vec4(0.04, 0.28, 0.26, 1.0); +} +</code></pre> + +<p> + Using the scene from the <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing" target="_blank">previous</a> chapter we're going to add object outlining to the two containers, so we'll leave the floor out of it. We want to first draw the floor, then the two containers (while writing to the stencil buffer), and then draw the scaled-up containers (while discarding the fragments that write over the previously drawn container fragments). +</p> + +<p> + We first need to enable stencil testing: +</p> + +<pre class="cpp"><code> +<function id='60'>glEnable</function>(GL_STENCIL_TEST); +</code></pre> + +<p> + And then in each frame we want to specify the action to take whenever any of the stencil tests succeed or fail: +</p> + +<pre class="cpp"><code> +<function id='69'>glStencilOp</function>(GL_KEEP, GL_KEEP, GL_REPLACE); +</code></pre> + +<p> + If any of the tests fail we do nothing; we simply keep the currently stored value that is in the stencil buffer. If both the stencil test and the depth test succeed however, we want to replace the stored stencil value with the reference value set via <fun><function id='68'>glStencilFunc</function></fun> which we later set to <code>1</code>. +</p> + +<p> + We clear the stencil buffer to <code>0</code>s at the start of the frame and for the containers we update the stencil buffer to <code>1</code> for each fragment drawn: +</p> + +<pre><code> +<function id='69'>glStencilOp</function>(GL_KEEP, GL_KEEP, GL_REPLACE); +<function id='68'>glStencilFunc</function>(GL_ALWAYS, 1, 0xFF); // all fragments should pass the stencil test +<function id='67'>glStencilMask</function>(0xFF); // enable writing to the stencil buffer +normalShader.use(); +DrawTwoContainers(); +</code></pre> + +<p> + By using <var>GL_REPLACE</var> as the stencil op function we make sure that each of the containers' fragments update the stencil buffer with a stencil value of <code>1</code>. Because the fragments always pass the stencil test, the stencil buffer is updated with the reference value wherever we've drawn them. +</p> + +<p> + Now that the stencil buffer is updated with <code>1</code>s where the containers were drawn we're going to draw the upscaled containers, but this time with the appropriate test function and disabling writes to the stencil buffer: +</p> + +<pre><code> +<function id='68'>glStencilFunc</function>(GL_NOTEQUAL, 1, 0xFF); +<function id='67'>glStencilMask</function>(0x00); // disable writing to the stencil buffer +glDisable(GL_DEPTH_TEST); +shaderSingleColor.use(); +DrawTwoScaledUpContainers(); +</code></pre> + +<p> + We set the stencil function to <var>GL_NOTEQUAL</var> to make sure that we're only drawing parts of the containers that are not equal to <code>1</code>. This way we only draw the part of the containers that are outside the previously drawn containers. Note that we also disable depth testing so the scaled up containers (e.g. the borders) do not get overwritten by the floor. Make sure to enable the depth buffer again once you're done. +</p> + +<p> + The total object outlining routine for our scene looks something like this: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +<function id='69'>glStencilOp</function>(GL_KEEP, GL_KEEP, GL_REPLACE); + +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + +<function id='67'>glStencilMask</function>(0x00); // make sure we don't update the stencil buffer while drawing the floor +normalShader.use(); +DrawFloor() + +<function id='68'>glStencilFunc</function>(GL_ALWAYS, 1, 0xFF); +<function id='67'>glStencilMask</function>(0xFF); +DrawTwoContainers(); + +<function id='68'>glStencilFunc</function>(GL_NOTEQUAL, 1, 0xFF); +<function id='67'>glStencilMask</function>(0x00); +glDisable(GL_DEPTH_TEST); +shaderSingleColor.use(); +DrawTwoScaledUpContainers(); +<function id='67'>glStencilMask</function>(0xFF); +<function id='68'>glStencilFunc</function>(GL_ALWAYS, 1, 0xFF); +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +</code></pre> + +<p> + As long as you understand the general idea behind stencil testing this shouldn't be too hard to understand. Otherwise try to carefully read the previous sections again and try to completely understand what each of the functions does now that you've seen an example of it can be used. +</p> + +<p> + The result of the outlining algorithm then looks like this: +</p> + + +<img src="/img/advanced/stencil_scene_outlined.png" class="clean" alt="3D scene with object outlining using a stencil buffer"/> + +<p> + Check the source code <a href="/code_viewer_gh.php?code=src/4.advanced_opengl/2.stencil_testing/stencil_testing.cpp" target="_blank">here</a> to see the complete code of the object outlining algorithm. +</p> + +<note> + You can see that the borders overlap between both containers which is usually the effect that we want (think of strategy games where we want to select 10 units; merging borders is generally preferred). If you want a complete border per object you'd have to clear the stencil buffer per object and get a little creative with the depth buffer. +</note> + +<p> + The object outlining algorithm you've seen is commonly used in games to visualize selected objects (think of strategy games) and an algorithm like this can easily be implemented within a model class. You could set a boolean flag within the model class to draw either with borders or without. If you want to be creative you could even give the borders a more natural look with the help of post-processing filters like Gaussian Blur. +</p> + +<p> + Stencil testing has many more purposes (beside outlining objects) like drawing textures inside a rear-view mirror so it neatly fits into the mirror shape, or rendering real-time shadows with a stencil buffer technique called <def>shadow volumes</def>. Stencil buffers give us with yet another nice tool in our already extensive OpenGL toolkit. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Code-repository.html b/pub/Code-repository.html @@ -0,0 +1,340 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Code repository</h1> +<h1 id="content-url" style='display:none;'>Code-repository</h1> +<p> + You can find all relevant code samples online in each chapter, but if you want to quickly run the chapter demos yourself or compare your code with working examples you can find an online code repository <a href="https://github.com/JoeyDeVries/LearnOpenGL" target="_blank">here</a> hosted on Github. +</p> + +<p> + At the moment the <code>CMakeLists.txt</code> file can properly generate visual studio project files, make-files and works on both Windows and Linux. It hasn't been extensively tested on Apple's OS X and not on all IDEs so feel free to leave a comment or update the <code>CMakeLists.txt</code> via a pull request if you can get it working on different systems. +</p> + +<p> + I'd like to thank Zwookie for being an enormous help on the Linux side of the CMake script. Thanks to Zwookie's updates on the CMakeLists it now succesfully generates project files on both Windows and Linux. +</p> + +<p> + Also, check out <a href="https://github.com/Polytonic/Glitter" target="_blank">Glitter</a> by Polytonic, a dead-simple boilerplate project for these chapters that comes pre-configured with the relevant dependencies. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Getting-started/Camera.html b/pub/Getting-started/Camera.html @@ -0,0 +1,988 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Camera</h1> + <h1 id="content-title">カメラ</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Camera</h1> +<p> + In the previous chapter we discussed the view matrix and how we can use the view matrix to move around the scene (we moved backwards a little). OpenGL by itself is not familiar with the concept of a <em>camera</em>, but we can try to simulate one by moving all objects in the scene in the reverse direction, giving the illusion that <strong>we</strong> are moving. + 前章において視野行列を用いて空間上を移動する方法を学びました(少しだけ後ろに下りました)。OpenGL自体にカメラの概念はありませんが、空間中の全ての物体を反対方向に動かすことでカメラを再現でき、<strong>自分自身</strong>が動いているように見せかけることができます。 +</p> + +<p> + In this chapter we'll discuss how we can set up a camera in OpenGL. We will discuss a fly style camera that allows you to freely move around in a 3D scene. We'll also discuss keyboard and mouse input and finish with a custom camera class. + 本章ではどのようにしてカメラを設定するかを見て行きます。ここでは3次元空間上を自由に動き回れるような飛行型のカメラについて議論します。キーボードやマウスからの入力についても議論し、最後に独自のカメラクラスを作成します。 +</p> + +<h2>Camera/View space</h2> +<h2>カメラ空間(視野空間)</h2> +<p> + When we're talking about camera/view space we're talking about all the vertex coordinates as seen from the camera's perspective as the origin of the scene: the view matrix transforms all the world coordinates into view coordinates that are relative to the camera's position and direction. To define a camera we need its position in world space, the direction it's looking at, a vector pointing to the right and a vector pointing upwards from the camera. A careful reader may notice that we're actually going to create a coordinate system with 3 perpendicular unit axes with the camera's position as the origin. + カメラ空間あるいは視野空間というのは全ての頂点の座標がカメラの位置を原点とし、カメラから見ているような空間です。視野行列が世界全体の座標を視野座標に変換します。この視野座標がカメラの位置と向いている方向に対する座標です。カメラを定義する為には大域空間におけるカメラの位置、向いている方向、カメラの右側を指すベクトル、そしてカメラの上側を指すベクトルが必要です。注意深い読者は、3つの互いに直交する単位ベクトルを用いてカメラの位置を原点とした座標空間を作成しようとしていることに気付くでしょう。 +</p> + +<img src="/img/getting-started/camera_axes.png" class="clean"/> + +<h3>1. Camera position</h3> +<h3>1. カメラの位置</h3> +<p> + Getting the camera position is easy. The camera position is a vector in world space that points to the camera's position. We set the camera at the same position we've set the camera in the previous chapter: + カメラの位置を取得するのは簡単です。カメラの位置は大域空間におけるカメラの位置を表わすベクトルです。今回カメラの位置は前章で設定したものと同じにしましょう: +</p> + +<pre><code> +glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f); +</code></pre> + +<note> + Don't forget that the positive z-axis is going through your screen towards you so if we want the camera to move backwards, we move along the positive z-axis. + z軸は画面の手前に伸びているので、カメラを手前に動かす場合、z軸正の方向を指定します。 +</note> + +<h3>2. Camera direction</h3> +<h2>2. カメラの方向</h2> +<p> + The next vector required is the camera's direction e.g. at what direction it is pointing at. For now we let the camera point to the origin of our scene: <code>(0,0,0)</code>. Remember that if we subtract two vectors from each other we get a vector that's the difference of these two vectors? Subtracting the camera position vector from the scene's origin vector thus results in the direction vector we want. For the view matrix's coordinate system we want its z-axis to be positive and because by convention (in OpenGL) the camera points towards the negative z-axis we want to negate the direction vector. If we switch the subtraction order around we now get a vector pointing towards the camera's positive z-axis: + 次に必要なのはカメラの向き、つまりカメラがどの点を見ているかです。とりあえずカメラが原点<code>(0, 0, 0)</code>を見ているとしましょう。ベクトルの引き算により、それらのベクトルの差が得られるのを覚えているでしょうか。カメラの位置を世界の原点から引き算すれば、カメラの向きを表わすベクトルが得られます。視野行列の座標系においてカメラの位置のz座標は正で、OpenGLの慣例によりカメラはz軸の負の方を向いているので、方向ベクトルの正負を反転させます。引き算の順序を反対にした場合、原点からカメラを向いた、z座標が正のベクトルが得られます: +</p> + +<pre><code> +glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f); +glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget); +</code></pre> + +<warning> + The name <em>direction</em> vector is not the best chosen name, since it is actually pointing in the reverse direction of what it is targeting. + <em>direction(方向)</em>というのは最良の名前とは言えません。このベクトルは実際にはカメラの向いているのと逆方向のベクトルだからです。 +</warning> + +<h3>3. Right axis</h3> +<h3>3. 右方向の軸</h3> +<p> + The next vector that we need is a <em>right</em> vector that represents the positive x-axis of the camera space. To get the <em>right</em> vector we use a little trick by first specifying an <em>up</em> vector that points upwards (in world space). Then we do a cross product on the up vector and the direction vector from step 2. Since the result of a cross product is a vector perpendicular to both vectors, we will get a vector that points in the positive x-axis's direction (if we would switch the cross product order we'd get a vector that points in the negative x-axis): + 次に必要なベクトルは<em>右方向</em>のベクトルです。このベクトルはカメラ空間のx軸正の方向を表わすものです。<em>右方向</em>のベクトルを得る為に少し技巧的なことをします。まず大域空間において<em>上向き</em>のベクトルを取り、このベクトルと、2段階目に作成した方向ベクトルの外積を取ります。外積の結果得られるベクトルは両方のベクトルに直交するので、この計算によりx軸正の方向を向いたベクトルが得られるのです(外積の掛ける順番を逆にするとベクトルはx軸負の方向を向きます)。 +</p> + +<pre><code> +glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); +glm::vec3 cameraRight = glm::normalize(<function id='61'>glm::cross</function>(up, cameraDirection)); +</code></pre> + +<h3>4. Up axis</h3> +<h3>4. 上方向の軸</h3> +<p> + Now that we have both the x-axis vector and the z-axis vector, retrieving the vector that points to the camera's positive y-axis is relatively easy: we take the cross product of the right and direction vector: + ここまででx軸とz軸のベクトルが得られたので、カメラの座標系におけるy軸正の方向のベクトルを計算するのは簡単です。右方向のベクトルとカメラの向いている方向のベクトルの外積を取ればいいのです: +</p> + +<pre><code> +glm::vec3 cameraUp = <function id='61'>glm::cross</function>(cameraDirection, cameraRight); +</code></pre> + +<p> + With the help of the cross product and a few tricks we were able to create all the vectors that form the view/camera space. For the more mathematically inclined readers, this process is known as the <a href="http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process" target="_blank">Gram-Schmidt</a> process in linear algebra. Using these camera vectors we can now create a <def>LookAt</def> matrix that proves very useful for creating a camera. + 外積により視野空間を形成するベクトルを全て取得できました。この手法は<a href="http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process" target="_blank">グラム・シュミットの正規直交化法</a>と言います。数学に傾倒した人はご自身で調べてみて下さい。これらのカメラに関するベクトルを利用することで、<def>視点行列(LookAt Matrix)</def>を作成できます。この行列はカメラを作成する上で非常に便利なものです。 +</p> + +<h2>Look At</h2> +<h2>視点</h2> +<p> + A great thing about matrices is that if you define a coordinate space using 3 perpendicular (or non-linear) axes you can create a matrix with those 3 axes plus a translation vector and you can transform any vector to that coordinate space by multiplying it with this matrix. This is exactly what the <em>LookAt</em> matrix does and now that we have 3 perpendicular axes and a position vector to define the camera space we can create our own LookAt matrix: + 互いに直交する(あるいは一次独立な)3つのベクトルにより座標空間を定義し、その3つの座標軸と平行移動ベクトルを用いると、任意のベクトルにその変換行列を掛けることで、その座標空間に変換できます。これはまさに<em>視点</em>行列が行なうことです。先程カメラ空間を定義する為に3つの直交するベクトルとカメラの位置ベクトルを作成しました。これらを用いて以下のように視点行列を作成できます: + + \[LookAt = \begin{bmatrix} \color{red}{R_x} & \color{red}{R_y} & \color{red}{R_z} & 0 \\ \color{green}{U_x} & \color{green}{U_y} & \color{green}{U_z} & 0 \\ \color{blue}{D_x} & \color{blue}{D_y} & \color{blue}{D_z} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} * \begin{bmatrix} 1 & 0 & 0 & -\color{purple}{P_x} \\ 0 & 1 & 0 & -\color{purple}{P_y} \\ 0 & 0 & 1 & -\color{purple}{P_z} \\ 0 & 0 & 0 & 1 \end{bmatrix} \] + + Where \({\color{red}R}\) is the right vector, \({\color{green}U}\) is the up vector, \({\color{blue}D}\) is the direction vector and \({\color{purple}P}\) is the camera's position vector. Note that the rotation (left matrix) and translation (right matrix) parts are inverted (transposed and negated respectively) since we want to rotate and translate the world in the opposite direction of where we want the camera to move. Using this LookAt matrix as our view matrix effectively transforms all the world coordinates to the view space we just defined. The LookAt matrix then does exactly what it says: it creates a view matrix that <em>looks</em> at a given target. + ここで、\({\color{red}R}\)はカメラの右方向のベクトル、\({\color{green}U}\)は上方向のベクトル、\({\color{blue}D}\)はカメラの方向ベクトル、そして\({\color{purple}P}\)はカメラの位置ベクトルです。左側の行列の回転と、右側の行列の平行移動がそれぞれ逆向きになっていることに注意して下さい。転置行列による回転と、符号が反転した平行移動にそれぞれなっています。カメラを移動させるのと反対方向に世界全体を回転、平行移動する為です。この視点行列を視野行列として用いることで、大域座標全体を今しがた定義した視野空間に変換できます。視点行列は与えられた位置を<em>見る</em>ような視野行列を作成します。視点行列は名前の通りの仕事をするのです。 +</p> + +<p> + Luckily for us, GLM already does all this work for us. We only have to specify a camera position, a target position and a vector that represents the up vector in world space (the up vector we used for calculating the right vector). GLM then creates the LookAt matrix that we can use as our view matrix: + 有り難いことにGLMが必要な仕事を全て行ってくれます。カメラの位置、視点の位置そして大域空間の上を示すベクトル(カメラの右を示すベクトルを作成するのに必要です)を指定するだけでいいのです。そうすると視野行列の作成に必要な視点行列をGLMが作成してくれます: +</p> + +<pre><code> +glm::mat4 view; +view = <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 3.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f)); +</code></pre> + +<p> + The <fun><function id='62'>glm::LookAt</function></fun> function requires a position, target and up vector respectively. This example creates a view matrix that is the same as the one we created in the previous chapter. +<fun><function id='62'>glm::LookAt</function></fun>には位置、視点、上のベクトルを渡します。この例では前章で作成したものと同じ視野行列を作成しています。 +</p> + +<p> + Before delving into user input, let's get a little funky first by rotating the camera around our scene. We keep the target of the scene at <code>(0,0,0)</code>. We use a little bit of trigonometry to create an <code>x</code> and <code>z</code> coordinate each frame that represents a point on a circle and we'll use these for our camera position. By re-calculating the <code>x</code> and <code>y</code> coordinate over time we're traversing all the points in a circle and thus the camera rotates around the scene. We enlarge this circle by a pre-defined <var>radius</var> and create a new view matrix each frame using GLFW's <fun><function id='47'>glfwGetTime</function></fun> function: + ユーザーからの入力に話を進める前に、少しイケたことをしてみましょう。カメラの視点を<code>(0, 0, 0)</code>に固定して、その周りで回転させてみます。三角関数を利用して各フレームに対してxz平面における円周上の点を求め、それをカメラの位置として利用します。<code>x</code>座標と<code>z</code>座標をフレーム毎に計算しなおすことで円周上をカメラが移動して行き、回転しているように見えます。あらかじめ定義しておいた<var>radius</var>という変数を用いてこの円周を大きくし、<fun><function id='47'>glfwGetTime</function></fun>を用いてフレーム毎に新しい視野行列を作成します: +</p> + +<pre><code> +const float radius = 10.0f; +float camX = sin(<function id='47'>glfwGetTime</function>()) * radius; +float camZ = cos(<function id='47'>glfwGetTime</function>()) * radius; +glm::mat4 view; +view = <function id='62'>glm::lookAt</function>(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0)); +</code></pre> + +<p> + If you run this code you should get something like this: + このコードを実行すると以下のようなものが得られます: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/camera_circle.mp4" type="video/mp4"/> + <img src="/img/getting-started/camera_circle.png" class="clean"/> + </video> +</div> + +<p> + With this little snippet of code the camera now circles around the scene over time. Feel free to experiment with the radius and position/direction parameters to get the feel of how this <em>LookAt</em> matrix works. Also, check the <a href="/code_viewer_gh.php?code=src/1.getting_started/7.1.camera_circle/camera_circle.cpp" target="_blank">source code</a> if you're stuck. + このちょっとしたコードで、時間と共にカメラを回転させることができました。半径や位置、方向等の変数を変化させてみて下さい。<em>視点行列</em>の働きが掴めるはずです。また、どこかで詰まった場合は<a href="/code_viewer_gh.php?code=src/1.getting_started/7.1.camera_circle/camera_circle.cpp" target="_blank">ソースコード</a>を確認して下さい。 +</p> + +<h1>Walk around</h1> +<h1>歩き回る</h1> +<p> + Swinging the camera around a scene is fun, but it's more fun to do all the movement ourselves! First we need to set up a camera system, so it is useful to define some camera variables at the top of our program: + カメラが動くのは楽しいものですが、それを自分自身で行なえたらもっと楽しいでしょう。まずカメラの座標系を設定しなければなりませんが、その為にカメラに関する変数をいくつかプログラムの冒頭に定義しておくのが便利です: +</p> + +<pre><code> +glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f); +glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); +glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); +</code></pre> + +<p> + The <code>LookAt</code> function now becomes: + これらを用いると<code>LookAt</code>関数は以下のようになります: +</p> + +<pre><code> +view = <function id='62'>glm::lookAt</function>(cameraPos, cameraPos + cameraFront, cameraUp); +</code></pre> + +<p> + First we set the camera position to the previously defined <var>cameraPos</var>. The direction is the current position + the direction vector we just defined. This ensures that however we move, the camera keeps looking at the target direction. Let's play a bit with these variables by updating the <var>cameraPos</var> vector when we press some keys. + まず初めに先程定義した<var>cameraPos</var>を用いてカメラの位置を設定します。方向は現在の位置ベクトルにカメラの方向ベクトルを足したものです。こうすることでカメラがどのように動いても同じ点を見続けるようになります。これを少し面白くするために、特定のキーを押下した時に<var>cameraPos</var>を変更するようにしてみましょう: +</p> + +<p> + We already defined a <fun>processInput</fun> function to manage GLFW's keyboard input so let's add a few extra key commands: + GLFWのキーボーッド入力を処理する為に<fun>processInput</fun>という関数を既に定義していました。これに他のキーの処理を追加します: +</p> + +<pre><code> +void processInput(GLFWwindow *window) +{ + ... + const float cameraSpeed = 0.05f; // adjust accordingly + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + cameraPos += cameraSpeed * cameraFront; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + cameraPos -= cameraSpeed * cameraFront; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + cameraPos -= glm::normalize(<function id='61'>glm::cross</function>(cameraFront, cameraUp)) * cameraSpeed; + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + cameraPos += glm::normalize(<function id='61'>glm::cross</function>(cameraFront, cameraUp)) * cameraSpeed; +} +</code></pre> + +<p> + Whenever we press one of the <code>WASD</code> keys, the camera's position is updated accordingly. If we want to move forward or backwards we add or subtract the direction vector from the position vector scaled by some speed value. If we want to move sideways we do a cross product to create a <em>right</em> vector and we move along the right vector accordingly. This creates the familiar <def>strafe</def> effect when using the camera. + <code>WASD</code>のいずれかのキーを押すと、押したキーに応じてカメラの位置が更新されます。前進あるいは後退したい時はカメラの向きのベクトルに速度の値を掛けたものをカメラの位置ベクトルに加え、あるいは引きます。横に移動したければ外積を取って<em>右方向</em>のベクトルを作り、その方向に移動します。この処理により、<def>機銃掃射(strafe)</def>効果が得られます。これは一点を見つめたまま横に移動するというもののようです。 +</p> + +<note> + Note that we normalize the resulting <em>right</em> vector. If we wouldn't normalize this vector, the resulting cross product may return differently sized vectors based on the <var>cameraFront</var> variable. If we would not normalize the vector we would move slow or fast based on the camera's orientation instead of at a consistent movement speed. + <em>右方向</em>のベクトルを正規化していることに注意して下さい。こうしないと外積の結果得られるベクトルの大きさが違ってしまします。カメラの向きによって移動速度が大きくなったり小さくなったりして一定しません。 +</note> + +<p> + By now, you should already be able to move the camera somewhat, albeit at a speed that's system-specific so you may need to adjust <var>cameraSpeed</var>. + ここまでで既にカメラを移動することはできるようになりました。とは言えその速度はシステムによって異なりますので、<var>cameraSpeed</var>を調整しないといけません。 +</p> + +<h2>Movement speed</h2> +<h2>移動速度</h2> +<p> + Currently we used a constant value for movement speed when walking around. In theory this seems fine, but in practice people's machines have different processing powers and the result of that is that some people are able to render much more frames than others each second. Whenever a user renders more frames than another user he also calls <fun>processInput</fun> more often. The result is that some people move really fast and some really slow depending on their setup. When shipping your application you want to make sure it runs the same on all kinds of hardware. + 現状移動速度は定数です。これでも良いように思われますが、実際はコンピュータの処理能力により1秒間に描画できるフレーム数が異なり、多くのフレームを描画できるコンピュータではそれだけ多く<fun>processInput</fun>が呼ばれるので、移動速度がコンピュータによりまちまちになってしまいます。アプリケーションを配布するなら、どのようなハードウェアにおいても同じように実行されるのが好ましいでしょう。 +</p> + +<p> + Graphics applications and games usually keep track of a <def>deltatime</def> variable that stores the time it took to render the last frame. We then multiply all velocities with this <var>deltaTime</var> value. The result is that when we have a large <var>deltaTime</var> in a frame, meaning that the last frame took longer than average, the velocity for that frame will also be a bit higher to balance it all out. When using this approach it does not matter if you have a very fast or slow pc, the velocity of the camera will be balanced out accordingly so each user will have the same experience. + グラフィックスを利用するアプリケーションやゲームは、通常直前のフレームの描画にかかった時間である<def>差分時間(deltatime)</def>という変数を追跡しています。ここではこの<var>deltaTime</var>の値を速度に掛け合せます。そうすることで直前のフレームの描画に長い時間かかり、<var>deltaTime</var>が大きくなった場合、そのフレームでの速度も大きくなり、描画時間の差が均されます。この方法によりPCの処理速度に関わらずカメラの速度が一定に保たれ、同じような体験が得られます。 +</p> + +<p> + To calculate the <var>deltaTime</var> value we keep track of 2 global variables: + <var>deltaTime</var>の値を計算する為に2つの大域変数を追跡します: +</p> + +<pre><code> +float deltaTime = 0.0f; // Time between current frame and last frame +float deltaTime = 0.0f; // 直前のフレームと現在のフレームの間の時間 +float lastFrame = 0.0f; // Time of last frame +float lastFrame = 0.0f; // 直前のフレームの時刻 +</code></pre> + +<p> + Within each frame we then calculate the new <var>deltaTime</var> value for later use: + 各フレームにおいて<var>deltaTime</var>を計算して後の利用に備えます: +</p> + +<pre><code> +float currentFrame = <function id='47'>glfwGetTime</function>(); +deltaTime = currentFrame - lastFrame; +lastFrame = currentFrame; +</code></pre> + +<p> + Now that we have <var>deltaTime</var> we can take it into account when calculating the velocities: + <var>deltaTime</var>が得られたので速度の計算に組込みます: +</p> + +<pre><code> +void processInput(GLFWwindow *window) +{ + float cameraSpeed = 2.5f * deltaTime; + [...] +} +</code></pre> + +<p> + Since we're using <var>deltaTime</var> the camera will now move at a constant speed of <code>2.5</code> units per second. Together with the previous section we should now have a much smoother and more consistent camera system for moving around the scene: + <var>deltaTime</var>を使っているのでカメラは秒速<code>2.5</code>というような一定の速度では動きません。前節で作成したものと合わせると、よりなめらかで一定の動きでカメラを移動できるようになります: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/camera_smooth.mp4" type="video/mp4" /> + <img src="/img/getting-started/camera_smooth.png" class="clean"/> + </video> +</div> + +<p> + And now we have a camera that walks and looks equally fast on any system. Again, check the <a href="/code_viewer_gh.php?code=src/1.getting_started/7.2.camera_keyboard_dt/camera_keyboard_dt.cpp" target="_blank">source code</a> if you're stuck. We'll see the <var>deltaTime</var> value frequently return with anything movement related. + これにてどんなシステム上でも同じように動き回れるカメラの実装が完了しました。どこかで詰まったら<a href="/code_viewer_gh.php?code=src/1.getting_started/7.2.camera_keyboard_dt/camera_keyboard_dt.cpp" target="_blank">ソースコード</a>を確認して下さい。以降においてもなんらかの動きを作成する場合、<var>deltaTime</var>をしばしば利用することになります。 +</p> + +<h1>Look around</h1> +<h1>視点の移動</h1> +<p> + Only using the keyboard keys to move around isn't that interesting. Especially since we can't turn around making the movement rather restricted. That's where the mouse comes in! + キーボードの入力により動き回るだけではそんなに面白くありません。カメラの向きを変更できない為に動きが制限されているからです。そこでマウスの登場です。 +</p> + +<p> + To look around the scene we have to change the <var>cameraFront</var> vector based on the input of the mouse. However, changing the direction vector based on mouse rotations is a little complicated and requires some trigonometry. If you do not understand the trigonometry, don't worry, you can just skip to the code sections and paste them in your code; you can always come back later if you want to know more. + あちこち見て回るにはマウスからの入力に応じて<var>cameraFront</var>を変更する必要があります。しかしマウスの回転に応じて方向ベクトルを変化させるのは少し煩雑で、三角関数が必要です。三角関数を知らない人も心配は不要です。コードの章まで読み飛してコピペして下さい。内容を知りたくなった時に戻って来れば結構です。 +</p> + +<h2>Euler angles</h2> +<h2>オイラー角</h2> +<p> + Euler angles are 3 values that can represent any rotation in 3D, defined by Leonhard Euler somewhere in the 1700s. There are 3 Euler angles: <em>pitch</em>, <em>yaw</em> and <em>roll</em>. The following image gives them a visual meaning: + オイラー角は3次元空間における任意の角度を表わすことができる3つの値です。1700年代にレオンハルト・オイラーによって定義されました。オイラー角を構成する3つの角はそれぞれ<em>仰角</em>、<em>方位角</em>、<em>傾斜角</em>です。これらの角を図示すると以下のようになります: +</p> + +<img src="/img/getting-started/camera_pitch_yaw_roll.png" alt="Euler angles yaw pitch and roll" class="clean"/> + +<p> + The <def>pitch</def> is the angle that depicts how much we're looking up or down as seen in the first image. The second image shows the <def>yaw</def> value which represents the magnitude we're looking to the left or to the right. The <def>roll</def> represents how much we <em>roll</em> as mostly used in space-flight cameras. Each of the Euler angles are represented by a single value and with the combination of all 3 of them we can calculate any rotation vector in 3D. + <def>仰角(pitch)</def>は1つ目の図の通り、どのくらい見上げているか、あるいは見下げているかを表わします。<def>方位角</def>は2つ目の図の通り、左右の角度です。<def>傾斜角</def>はどのくらい<em>傾いている</em>かを表わし、特に空間中を飛んでいるカメラのようなものに利用されます。これら3つの角の組み合わせにより3次元空間上の任意の回転を表わせます。 +</p> + +<p> + For our camera system we only care about the yaw and pitch values so we won't discuss the roll value here. Given a pitch and a yaw value we can convert them into a 3D vector that represents a new direction vector. The process of converting yaw and pitch values to a direction vector requires a bit of trigonometry. and we start with a basic case: + これから作成するカメラにおいては仰角と方位角のみを考えますので、傾斜角については議論しません。仰角と方位角が与えられると、その方向を向いた3次元のベクトルを作成できます。仰角と方位角の値を方向ベクトルに変換するために、三角関数の知識が必要です。まずは基本的な場合から始めましょう: +</p> + +<p> + Let's start with a bit of a refresher and check the general right triangle case (with one side at a 90 degree angle): + 復習の意味も兼ねて、一般的な直角三角形を用いて説明します: + +<img src="/img/getting-started/camera_triangle.png" class="clean"/> + +<p> + If we define the hypotenuse to be of length <code>1</code> we know from trigonometry (soh cah toa) that the adjacant side's length is \(\cos \ {\color{red}x}/{\color{purple}h} = \cos \ {\color{red}x}/{\color{purple}1} = \cos\ {\color{red}x}\) and that the opposing side's length is \(\sin \ {\color{green}y}/{\color{purple}h} = \sin \ {\color{green}y}/{\color{purple}1} = \sin\ {\color{green}y.}\) This gives us some general formulas for retrieving the length in both the <code>x</code> and <code>y</code> sides on right triangles, depending on the given angle. Let's use this to calculate the components of the direction vector. + 斜辺の長さを<code>1</code>とした場合、三角関数を使うと、底辺の長さは\(\cos \ {\color{red}x}/{\color{purple}h} = \cos \ {\color{red}x}/{\color{purple}1} = \cos \ {\color{red}x}\)、高さは\(\sin \ {\color{green}y}/{\color{purple}h} = \sin \ {\color{green}y}/{\color{purple}1} = \sin \ {\color{green}y}\)となります。 +</p> + +<p> + Let's imagine this same triangle, but now looking at it from a top perspective with the adjacent and opposite sides being parallel to the scene's x and z axis (as if looking down the y-axis). + これと同じ三角形を空間上で考えましょう。座標空間を上から見下ろし、三角形の底辺と対辺をそれぞれ空間のx軸とz軸に平行になるように置きます。 +</p> + +<img src="/img/getting-started/camera_yaw.png" class="clean"/> + +<p> + If we visualize the yaw angle to be the counter-clockwise angle starting from the <code>x</code> side we can see that the length of the <code>x</code> side relates to <code>cos(yaw)</code>. And similarly how the length of the <code>z</code> side relates to <code>sin(yaw)</code>. + 方位角を<code>x</code>軸から反時計回りの角度として定義すると、<code>x</code>軸の辺の長さは<code>cos(yaw)</code>となり、同様に<code>z</code>軸の辺は<code>sin(yaw)</code>となります。 +</p> + +<p> + If we take this knowledge and a given <code>yaw</code> value we can use it to create a camera direction vector: + このことを用いると、<code>yaw</code>の値から以下のようにカメラの方向ベクトルを作成できます: +</p> + +<pre><code> +glm::vec3 direction; +direction.x = cos(<function id='63'>glm::radians</function>(yaw)); // Note that we convert the angle to radians first +direction.x = cos(<function id='63'>glm::radians</function>(yaw)); // 角度の値をラジアンに変換していることに注意 +direction.z = sin(<function id='63'>glm::radians</function>(yaw)); +</code></pre> + +<p> + This solves how we can get a 3D direction vector from a yaw value, but pitch needs to be included as well. Let's now look at the <code>y</code> axis side as if we're sitting on the <code>xz</code> plane: + これで方位角から3次元の方向ベクトルを得られますが、これに加えて仰角も考慮する必要があります。今度は<code>xz</code>平面から<code>y</code>軸を眺めてみましょう: +</p> + + +<img src="/img/getting-started/camera_pitch.png" class="clean"/> + +<p> + Similarly, from this triangle we can see that the direction's y component equals <code>sin(pitch)</code> so let's fill that in: + 同様にしてこの三角形から、方向ベクトルのy要素は<code>sin(pitch)</code>となります: +</p> + + +<pre><code> +direction.y = sin(<function id='63'>glm::radians</function>(pitch)); +</code></pre> + +<p> + However, from the pitch triangle we can also see the <code>xz</code> sides are influenced by <code>cos(pitch)</code> so we need to make sure this is also part of the direction vector. With this included we get the final direction vector as translated from yaw and pitch Euler angles: + 加えてこの仰角を表わす三角形から<code>xz</code>平面の成分は<code>cos(pitch)</code>となるので、これを方向ベクトルに組込む必要があります。以上を踏まえると方向ベクトルは以下のようになります: +</p> + +<pre><code> +direction.x = cos(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); +direction.y = sin(<function id='63'>glm::radians</function>(pitch)); +direction.z = sin(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); +</code></pre> + +<p> + This gives us a formula to convert yaw and pitch values to a 3-dimensional direction vector that we can use for looking around. + これが仰角と方位角を、3次元の方向ベクトルに変換する公式です。このベクトルをカメラの視線として利用できます。 +</p> + +<p> + We've set up the scene world so everything's positioned in the direction of the negative z-axis. However, if we look at the <code>x</code> and <code>z</code> yaw triangle we see that a \(\theta\) of <code>0</code> results in the camera's <code>direction</code> vector to point towards the positive x-axis. To make sure the camera points towards the negative z-axis by default we can give the <code>yaw</code> a default value of a 90 degree clockwise rotation. Positive degrees rotate counter-clockwise so we set the default <code>yaw</code> value to: + 以前設定した空間において、全ての物体はz軸が負の位置に置かれていました。しかし<code>xz</code>平面における方位角を表わす三角形を見ると、\(\theta\)が<code>0</code>である場合カメラの<code>direction</code>ベクトルがx軸正の方向を向くことが分かります。初期段階においてカメラがz軸負の方向を見るようにするために、<code>yaw</code>の初期値を時計回りに90度回転させるべきです。正の値は反時計回りの回転を表わすので、<code>yaw</code>の初期値は以下のように設定します: +</p> + +<pre><code> +yaw = -90.0f; +</code></pre> + +<p> + You've probably wondered by now: how do we set and modify these yaw and pitch values? + それではどのようにしてこの仰角と方位角を更新すればよいでしょうか。 +</p> + + +<h2>Mouse input</h2> +<h2>マウスからの入力</h2> +<p> + The yaw and pitch values are obtained from mouse (or controller/joystick) movement where horizontal mouse-movement affects the yaw and vertical mouse-movement affects the pitch. The idea is to store the last frame's mouse positions and calculate in the current frame how much the mouse values changed. The higher the horizontal or vertical difference, the more we update the pitch or yaw value and thus the more the camera should move. + 方位角と仰角の値はマウス(あるいはコントローラ、ジョイスティック等)の動きから得ます。マウスの水平方向の移動により方位角を、垂直方向の移動により仰角を変更するようにします。直前のフレームにおけるマウスの位置を保存しておいて、現在のフレームにおけるマウスの位置との差を計算します。水平方向や垂直方向の動きが大きいほど、方位角や仰角の変化量も大きくなるようにし、カメラも大きく動かされるようにしましょう。 +</p> + +<p> + First we will tell GLFW that it should hide the cursor and <def>capture</def> it. Capturing a cursor means that, once the application has focus, the mouse cursor stays within the center of the window (unless the application loses focus or quits). We can do this with one simple configuration call: + まずはGLFWがカーソルを<def>キャプチャ</def>し非表示にするようにします。カーソルのキャプチャとはアプリケーションにフォーカスしたらマウスのカーソルを(別のアプリケーションにフォーカスが移るか、このアプリケーションが終了するまで)ウィンドウの中央に固定しておくことを言います。これは1つの簡単な設定により実行できます: +</p> + +<pre><code> +glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); +</code></pre> + +<p> + After this call, wherever we move the mouse it won't be visible and it should not leave the window. This is perfect for an FPS camera system. + この関数を実行することで、マウスを動かしても表示されず、ウィンドウの外に出ることもなくなります。FPSのカメラには最適です。 +</p> + +<p> +To calculate the pitch and yaw values we need to tell GLFW to listen to mouse-movement events. We do this by creating a callback function with the following prototype: +仰角と方位角を計算する為、GLFWにマウスのイベントを監視させる必要があります。その為に以下のようなプロトタイプのコールバック関数を定義します: +</p> + +<pre><code> +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +</code></pre> + +<p> + Here <var>xpos</var> and <var>ypos</var> represent the current mouse positions. As soon as we register the callback function with GLFW each time the mouse moves, the <fun>mouse_callback</fun> function is called: + ここで、<var>xpos</var>と<var>ypos</var>は現在のマウスの位置を表わします。この関数をGLFWにコールバックとして登録すれば、マウスが動く度に<fun>mouse_callback</fun>が呼ばれます: +</p> + +<pre><code> +glfwSetCursorPosCallback(window, mouse_callback); +</code></pre> + +<p> + When handling mouse input for a fly style camera there are several steps we have to take before we're able to fully calculate the camera's direction vector: + マウスの入力により飛行型のカメラを操作する場合、実際にカメラの方向ベクトルを計算する上で以下の手順を踏まなければいけません: + + <ol> + <li>Calculate the mouse's offset since the last frame.</li> + <li>直前のフレームからのマウスの移動量を計算。</li> + <li>Add the offset values to the camera's yaw and pitch values.</li> + <li>その移動量をカメラの方位角と仰角に加算。</li> + <li>Add some constraints to the minimum/maximum pitch values.</li> + <li>仰角をある範囲に制限。</li> + <li>Calculate the direction vector.</li> + <li>方向ベクトルを計算。</li> + </ol> +</p> + +<p> + The first step is to calculate the offset of the mouse since last frame. We first have to store the last mouse positions in the application, which we initialize to be in the center of the screen (screen size is <code>800</code> by <code>600</code>) initially: + 最初の作業は直近のフレームからのマウスの移動量を計算することです。まず直前のマウスの位置をアプリケーションに保存します。この変数の初期値は画面の中心にしておきます(画面の大きさは<code>800x600</code>です)。 +</p> + +<pre class="cpp"><code> +float lastX = 400, lastY = 300; +</code></pre> + +<p> + Then in the mouse's callback function we calculate the offset movement between the last and current frame: + 次にマウスのコールバック関数内で直前のフレームからの移動量を計算します: +</p> + +<pre><code> +float xoffset = xpos - lastX; +float yoffset = lastY - ypos; // reversed since y-coordinates range from bottom to top +float yoffset = lastY - ypos; // y座標は下から上へ増加するのでx座標とは逆 +lastX = xpos; +lastY = ypos; + +const float sensitivity = 0.1f; +xoffset *= sensitivity; +yoffset *= sensitivity; +</code></pre> + +<p> + Note that we multiply the offset values by a <var>sensitivity</var> value. If we omit this multiplication the mouse movement would be way too strong; fiddle around with the sensitivity value to your liking. + <var>sensitivity</var>(感度)を移動量の値に掛けていることに注意して下さい。これをしないとマウスによる動きが速すぎるかと思います。感度を変化させて好みの値を探って下さい。 +</p> + +<p> + Next we add the offset values to the globally declared <var>pitch</var> and <var>yaw</var> values: + 続いて大域的に宣言されている<var>pitch</var>と<var>yaw</var>に加算します: +</p> + +<pre><code> +yaw += xoffset; +pitch += yoffset; +</code></pre> + +<p> + In the third step we'd like to add some constraints to the camera so users won't be able to make weird camera movements (also causes a LookAt flip once direction vector is parallel to the world up direction). The pitch needs to be constrained in such a way that users won't be able to look higher than <code>89</code> degrees (at <code>90</code> degrees we get the LookAt flip) and also not below <code>-89</code> degrees. This ensures the user will be able to look up to the sky or below to his feet but not further. The constraints work by replacing the Euler value with its constraint value whenever it breaches the constraint: + 3つ目の作業はカメラの動く範囲に制限を加え、ユーザーがおかしなカメラの動きをできないようにすることです。カメラの方向ベクトルが大域空間の上向きのベクトルと平行になった時に視線がひっくり返るのです。すなわち仰角が<code>90</code>度になると視線の反転が起こるので、この角を<code>89</code>度以下、そして<code>-89</code>度以上に制限します。こうすることでユーザーは空を見上げたり足元を見下げたりはできますがそれ以上はできなくなります。このような制限は、この角が制限を越えたときに制限の最大値あるいは最小値に置き換えることで実現できます: +</p> + +<pre><code> +if(pitch &gt; 89.0f) + pitch = 89.0f; +if(pitch &lt; -89.0f) + pitch = -89.0f; +</code></pre> + +<p> + Note that we set no constraint on the yaw value since we don't want to constrain the user in horizontal rotation. However, it's just as easy to add a constraint to the yaw as well if you feel like it. + ここでは水平方向のカメラの動きに制限を設けたくないので、方位角の値には制限をかけません。しかし必要であれば仰角と同様の方法で制限することができます。 +</p> + +<p> + The fourth and last step is to calculate the actual direction vector using the formula from the previous section: + 最後の作業は方向ベクトルを前章の公式から計算することです: +</p> + +<pre><code> +glm::vec3 direction; +direction.x = cos(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); +direction.y = sin(<function id='63'>glm::radians</function>(pitch)); +direction.z = sin(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); +cameraFront = glm::normalize(direction); +</code></pre> + + <p> + This computed direction vector then contains all the rotations calculated from the mouse's movement. Since the <var>cameraFront</var> vector is already included in glm's <fun>lookAt</fun> function we're set to go. + この計算によりマウスの動きを加味した方向ベクトルが得られます。<var>cameraFront</var>ベクトルは既にglmの<fun>lookAt</fun>関数に含まれているので、これで準備完了です。 +</p> + +<p> + If you'd now run the code you'll notice the camera makes a large sudden jump whenever the window first receives focus of your mouse cursor. The cause for this sudden jump is that as soon as your cursor enters the window the mouse callback function is called with an <var>xpos</var> and <var>ypos</var> position equal to the location your mouse entered the screen from. This is often a position that is significantly far away from the center of the screen, resulting in large offsets and thus a large movement jump. We can circumvent this issue by defining a global <code>bool</code> variable to check if this is the first time we receive mouse input. If it is the first time, we update the initial mouse positions to the new <var>xpos</var> and <code>ypos</code> values. The resulting mouse movements will then use the newly entered mouse's position coordinates to calculate the offsets: + 現段階でのコードを実行すると最初にウィンドウにマウスのカーソルでフォーカスした際にカメラの視点が大きく移動することに気付くでしょう。マウスのカーソルがウィンドウに入った時にマウスのコールバック関数が呼ばれますがこの時引数として渡される<var>xpos</var>と<var>ypos</var>がその時のカーソルの位置であり、ウィンドウの中心からは大きく離れているため、中心から大きく移動したものと見做され、結果としてカメラの大きな移動になります。この問題に対処する為に、マウスからの入力を受けとるのが初めてかどうかを保存した大域的な真偽値の変数を定義します。マウスからの最初の入力があった場合、マウスの初期位置を引数として渡された<var>xpos</var>と<var>ypos</var>に変更します。そうするとマウスの移動量の計算に新しく入力されたマウスの位置が利用されるようになります: +</p> + +<pre><code> +if (firstMouse) // initially set to true +{ + lastX = xpos; + lastY = ypos; + firstMouse = false; +} +</code></pre> + +<p> + The final code then becomes: +</p> + +<pre><code> +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; + lastX = xpos; + lastY = ypos; + + float sensitivity = 0.1f; + xoffset *= sensitivity; + yoffset *= sensitivity; + + yaw += xoffset; + pitch += yoffset; + + if(pitch &gt; 89.0f) + pitch = 89.0f; + if(pitch &lt; -89.0f) + pitch = -89.0f; + + glm::vec3 direction; + direction.x = cos(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); + direction.y = sin(<function id='63'>glm::radians</function>(pitch)); + direction.z = sin(<function id='63'>glm::radians</function>(yaw)) * cos(<function id='63'>glm::radians</function>(pitch)); + cameraFront = glm::normalize(direction); +} +</code></pre> + +<p> + There we go! Give it a spin and you'll see that we can now freely move through our 3D scene! +これで完成です。コードを実行して3次元空間を自由に動き回れるか確認してみましょう。 +</p> + + +<h2>Zoom</h2> +<h2>拡大</h2> +<p> + As a little extra to the camera system we'll also implement a zooming interface. In the previous chapter we said the <em>Field of view</em> or <em>fov</em> largely defines how much we can see of the scene. When the field of view becomes smaller, the scene's projected space gets smaller. This smaller space is projected over the same NDC, giving the illusion of zooming in. To zoom in, we're going to use the mouse's scroll wheel. Similar to mouse movement and keyboard input we have a callback function for mouse scrolling: + 最後におまけとしてカメラに拡大機能を実装してみましょう。前章において、<em>視野角(fov)</em>が画面上で見える範囲を主に規定することをお話ししました。視野角が小さくなると空間の中で射影される範囲が小さくなります。範囲が狭くなった空間もそれまでと同じ大きさのNDCに投影されるので、結果として拡大したように見えます。ここでは拡大の操作をするためにマウスのホイールを利用しましょう。マウスの動きやキーボードの入力の時と同様に、マウスのスクロールに対してもコールバック関数を定義します。 +</p> + +<pre><code> +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + fov -= (float)yoffset; + if (fov &lt 1.0f) + fov = 1.0f; + if (fov &gt; 45.0f) + fov = 45.0f; +} +</code></pre> + +<p> + When scrolling, the <var>yoffset</var> value tells us the amount we scrolled vertically. When the <fun>scroll_callback</fun> function is called we change the content of the globally declared <var>fov</var> variable. Since <code>45.0</code> is the default fov value we want to constrain the zoom level between <code>1.0</code> and <code> 45.0</code>. + スクロールすると<var>yoffset</var>の値がその時のスクロール量を保持します。<fun>scroll_callback</fun>関数が呼ばれた時に、大域的に宣言されている<var>fov</var>の値を変更するようにします。<code>45.0</code>が視野角の既定値ですが、これを<code>1.0</code>から<code>45.0</code>の間で動かせるようにしましょう。 +</p> + +<p> + We now have to upload the perspective projection matrix to the GPU each frame, but this time with the <var>fov</var> variable as its field of view: + 透視投影行列をフレーム毎に更新する必要がありますが、この時に視野角の値として<var>fov</var>を利用するようにします: +</p> + +<pre><code> +projection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(fov), 800.0f / 600.0f, 0.1f, 100.0f); +</code></pre> + +<p> + And lastly don't forget to register the scroll callback function: + 最後にスクロールのコールバック関数を登録するのを忘れないようにしましょう: +</p> + +<pre><code> +<function id='64'>glfwSetScrollCallback</function>(window, scroll_callback); +</code></pre> + +<p> + And there you have it. We implemented a simple camera system that allows for free movement in a 3D environment. + これで完成です。3次元空間上を自由に動き回れるカメラが実装できました。 +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/camera_mouse.mp4" type="video/mp4" /> + <img src="/img/getting-started/camera_mouse.png" class="clean"/> + </video> +</div> + +<p> + Feel free to experiment a little and if you're stuck compare your code with the <a href="/code_viewer_gh.php?code=src/1.getting_started/7.3.camera_mouse_zoom/camera_mouse_zoom.cpp" target="_blank">source code</a>. + いろいろ変更して好きに実験してみて下さい。どこかで分からなくなったらご自分のコードとこの<a href="/code_viewer_gh.php?code=src/1.getting_started/7.3.camera_mouse_zoom/camera_mouse_zoom.cpp" target="_blank">ソースコード</a>を比較して下さい。 +</p> + +<h1>Camera class</h1> +<h1>カメラのクラス</h1> +<p> + In the upcoming chapters we'll always use a camera to easily look around the scenes and see the results from all angles. However, since the camera code can take up a significant amount of space on each chapter we'll abstract its details a little and create our own camera object that does most of the work for us with some neat little extras. Unlike the Shader chapter we won't walk you through creating the camera class, but provide you with the (fully commented) source code if you want to know the inner workings. + 以降の章では作成したものを様々な角度から確認するためにカメラを利用します。しかしカメラのコードは場所をとるので細かい部分を少し抽象化するためにカメラオブジェクトを作成し、コードをすっきりさせるようにします。シェーダーの章においては細かく解説しましたが今回は(コメントをたくさん付けた)ソースコードをご自身で見て内容を確認していただくことにします。 +</p> + +<p> + Like the <code>Shader</code> object, we define the camera class entirely in a single header file. You can find the camera class <a href="/code_viewer_gh.php?code=includes/learnopengl/camera.h" target="_blank">here</a>; you should be able to understand the code after this chapter. It is advised to at least check the class out once as an example on how you could create your own camera system. + <code>Shader</code>オブジェクトと同様、カメラのクラスはひとつのヘッダファイルに記述します。<a href="/code_viewer_gh.php?code=includes/learnopengl/camera.h" target="_blank">ここ</a>でカメラクラスを確認して下さい。ここまで読み終えた方には十分理解できるはずです。一度はこのクラスの中身にさっと目を通してカメラのシステムの作成方法を確認して下さい。 +</p> + +<warning> + The camera system we introduced is a fly like camera that suits most purposes and works well with Euler angles, but be careful when creating different camera systems like an FPS camera, or a flight simulation camera. Each camera system has its own tricks and quirks so be sure to read up on them. For example, this fly camera doesn't allow for pitch values higher than or equal to <code>90</code> degrees and a static up vector of <code>(0,1,0)</code> doesn't work when we take roll values into account. + ここで作成したカメラは飛んでいるようなカメラで、オイラー角と共にほとんどの目的に利用できます。しかしFPSのカメラやフライトシミュレーションのカメラのような別のカメラを作成する場合は注意が必要です。角カメラにはそれぞれ独特の手法が必要なので、利用する場合はそのカメラについての解説を読むようにして下さい。例えばここで作ったカメラは仰角を<code>90</code>度以上にできませんし、また傾斜角を考慮したい場合はカメラの上方向のベクトルを<code>(0, 1, 0)</code>に保っているのは間違いです。 +</warning> + +<p> + The updated version of the source code using the new camera object can be found <a href="/code_viewer_gh.php?code=src/1.getting_started/7.4.camera_class/camera_class.cpp" target="_blank">here</a>. + カメラオブジェクトを利用したコードは<a href="/code_viewer_gh.php?code=src/1.getting_started/7.4.camera_class/camera_class.cpp" target="_blank">こちら</a>です。 +</p> + +<h2>Exercises</h2> +<h2>演習</h2> +<p> + <ul> + <li>See if you can transform the camera class in such a way that it becomes a <strong>true</strong> fps camera where you cannot fly; you can only look around while staying on the <code>xz</code> plane: <a href="/code_viewer_gh.php?code=src/1.getting_started/7.5.camera_exercise1/camera_exercise1.cpp" target="_blank">solution</a>.</li> + <li>カメラクラスを変更して<strong>本当の</strong>FPSのカメラを実装して下さい。すなわち周囲を見回せても<code>xz</code>平面を離れられないようにして下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/7.5.camera_exercise1/camera_exercise1.cpp" target="_blank">解答</a>。</li> + <li>Try to create your own LookAt function where you manually create a view matrix as discussed at the start of this chapter. Replace glm's LookAt function with your own implementation and see if it still acts the same: <a href="/code_viewer_gh.php?code=src/1.getting_started/7.6.camera_exercise2/camera_exercise2.cpp" target="_blank">solution</a>.</li> + <li>この章の初めに紹介した視野行列を自身で作成し、それを利用してLookAt関数を実装して下さい。glmのLookAt関数を自身で作成したものに置き換え、同じように機能するか確認して下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/7.6.camera_exercise2/camera_exercise2.cpp" target="_blank">解答</a>。</li> + </ul> +</p> + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Coordinate-Systems.html b/pub/Getting-started/Coordinate-Systems.html @@ -0,0 +1,884 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Coordinate Systems</h1> + <h1 id="content-title">座標系</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Coordinate-Systems</h1> +<p> + In the last chapter we learned how we can use matrices to our advantage by transforming all vertices with transformation matrices. OpenGL expects all the vertices, that we want to become visible, to be in normalized device coordinates after each vertex shader run. That is, the <code>x</code>, <code>y</code> and <code>z</code> coordinates of each vertex should be between <code>-1.0</code> and <code>1.0</code>; coordinates outside this range will not be visible. What we usually do, is specify the coordinates in a range (or space) we determine ourselves and in the vertex shader transform these coordinates to normalized device coordinates (NDC). These NDC are then given to the rasterizer to transform them to 2D coordinates/pixels on your screen. + 前章において行列の便利な使い方、すなわち全ての頂点を変換行列により座標変換する方法を学びました。OpenGLでは画面に表示すべき全ての頂点は頂点シェーダーから出力される段階で正規化座標に納まっていないといけません。つまり各頂点の<code>x</code>、<code>y</code>、<code>z</code>座標が<code>-1.0</code>と<code>1.0</code>の間にないといけないということです。この範囲外のものは表示されません。開発者がすべきことは、表示したい範囲を決定し、頂点シェーダーにおいてその座標を正規化座標(NDC)に変換することです。そうすればNDCがラスターライザに渡され、画面に対応した2次元座標、あるいはピクセルに変換されます。 +</p> + +<p> + Transforming coordinates to NDC is usually accomplished in a step-by-step fashion where we transform an object's vertices to several coordinate systems before finally transforming them to NDC. The advantage of transforming them to several <em>intermediate</em> coordinate systems is that some operations/calculations are easier in certain coordinate systems as will soon become apparent. There are a total of 5 different coordinate systems that are of importance to us: + 座標をNDCに変換する作業は段階的に行われます。NDCに落とし込まれるまでに物体の頂点はいくつかの座標系を経由するのです。<em>中間的な</em>座標系を経由するのは、後程説明するようにある種の操作や計算が、特定の座標系において行うのが適しているからです。重要な座標系が5つあります: +</p> + + <ul> + <li>Local space (or Object space)</li> + <li>局所空間(物体空間)</li> + <li>World space</li> + <li>大域空間</li> + <li>View space (or Eye space)</li> + <li>視野空間</li> + <li>Clip space</li> + <li>クリップ空間</li> + <li>Screen space</li> + <li>スクリーン空間</li> + </ul> + +<p> + Those are all a different state at which our vertices will be transformed in before finally ending up as fragments. + 上記の空間は頂点が座標変換によってフラグメントになるまでに通過するものです。 +</p> + +<p> + You're probably quite confused by now by what a space or coordinate system actually is so we'll explain them in a more high-level fashion first by showing the total picture and what each specific space represents. + これらの空間あるいは座標系がいったい何のことなのかと混乱していることでしょう。これからもう少し詳しく説明します。まずは各空間が何を表しているのか、全体像を提示するところから始めます。 +</p> + +<h2>The global picture</h2> +<h2>全体像</h2> +<p> + To transform the coordinates from one space to the next coordinate space we'll use several transformation matrices of which the most important are the <def>model</def>, <def>view</def> and <def>projection</def> matrix. Our vertex coordinates first start in <def>local space</def> as <def>local coordinates</def> and are then further processed to <def>world coordinates</def>, <def>view coordinates</def>, <def>clip coordinates</def> and eventually end up as <def>screen coordinates</def>. The following image displays the process and shows what each transformation does: + ある座標系から次の座標系に変換する際、変換行列をいくつか用います。中でも重要なのが、<def>モデル</def>、<def>ビュー</def>、<def>射影変換</def>行列です。 + +</p> + +<img src="/img/getting-started/coordinate_systems.png" class="clean"/> + + <ol> + <li>Local coordinates are the coordinates of your object relative to its local origin; they're the coordinates your object begins in. </li> + <li>局所座標は物体ごとの座標です。各物体が自身の局所座標中で定義されます。</li> + <li>The next step is to transform the local coordinates to world-space coordinates which are coordinates in respect of a larger world. These coordinates are relative to some global origin of the world, together with many other objects also placed relative to this world's origin.</li> + <li>次の作業は局所座標を大域座標に変換することです。大域座標は大きな世界の座標です。この座標は世界のある原点に対する相対的な位置で表され、多くの物体が配置されるものです。</li> + <li>Next we transform the world coordinates to view-space coordinates in such a way that each coordinate is as seen from the camera or viewer's point of view. </li> + <li>続いて大域座標を視野座標に変換します。この座標系はカメラから見たときの座標です。</li> + <li>After the coordinates are in view space we want to project them to clip coordinates. Clip coordinates are processed to the <code>-1.0</code> and <code>1.0</code> range and determine which vertices will end up on the screen. Projection to clip-space coordinates can add perspective if using perspective projection. </li> + <li>座標が視野空間に納まったら、クリップ座標にそれを投射します。クリップ座標系は<code>-1.0</code>と<code>1.0</code>の範囲の座標系で、最終的に画面に表示されるものを決定します。透視投影を利用するのであればこのクリップ座標系への投射の段階で行います。</li> + <li> + And lastly we transform the clip coordinates to screen coordinates in a process we call <def>viewport transform</def> that transforms the coordinates from <code>-1.0</code> and <code>1.0</code> to the coordinate range defined by <fun><function id='22'>glViewport</function></fun>. The resulting coordinates are then sent to the rasterizer to turn them into fragments. + </li> + <li>最後に、クリップ座標を画面の座標系に変換します。この処理は<def>ビューポート変換</def>と呼ばれ、<code>-1.0</code>から<code>1.0</code>の座標を<fun><function id='22'>glViewport</function></fun>で定義された範囲の座標に変換するものです。変換を終えた座標はラスタライザに送られ、フラグメントに加工されます。</li> + </ol> + +<p> + You probably got a slight idea what each individual space is used for. The reason we're transforming our vertices into all these different spaces is that some operations make more sense or are easier to use in certain coordinate systems. For example, when modifying your object it makes most sense to do this in local space, while calculating certain operations on the object with respect to the position of other objects makes most sense in world coordinates and so on. If we want, we could define one transformation matrix that goes from local space to clip space all in one go, but that leaves us with less flexibility. + それぞれの空間がなにを意味するのか多少雰囲気を掴めたでしょうか。頂点をいくつもの空間に変換していくのは、ある種の処理が特定の空間において良く意味をなし、あるいはその空間において利用しやすいからです。例えば物体自体を変形させる操作はその物体の局所座標においてするのがいいでしょうし、他の物体との位置関係を計算するのであれば大域座標においてするのが分かりやすいでしょう。もちろん局所座標からクリップ座標への変換にひとつの変換行列だけを用いることもできますが、それでは柔軟性が非常に低くなります。 +</p> + +<p> + We'll discuss each coordinate system in more detail below. + それでは各座標空間について更に詳しく見ていきましょう。 +</p> + +<h2>Local space</h2> +<h2>局所空間</h2> +<p> + Local space is the coordinate space that is local to your object, i.e. where your object begins in. Imagine that you've created your cube in a modeling software package (like Blender). The origin of your cube is probably at <code>(0,0,0)</code> even though your cube may end up at a different location in your final application. Probably all the models you've created all have <code>(0,0,0)</code> as their initial position. All the vertices of your model are therefore in <em>local</em> space: they are all local to your object. + 局所空間は物体に結び付いた座標系です。ブレンダーのようなモデリングソフトで立方体を作成しているところを想像して下さい。最終的にアプリケーションの中で立方体がどこに置かれるか分かりませんが、モデリングソフトにおいては立方体の原点は<code>(0, 0, 0)</code>でしょう。他の全ての物体に関しても、作成時の最初の座標は<code>(0, 0, 0)</code>であるはずです。作成した物体の頂点の座標は全てその物体に<em>固有</em>のものであるということです。 +</p> + +<p> + The vertices of the container we've been using were specified as coordinates between <code>-0.5</code> and <code>0.5</code> with <code>0.0</code> as its origin. These are local coordinates. + 今回使っている箱の頂点の座標は<code>-0.5</code>と<code>0.5</code>の間にあり、原点は<code>0.0</code>ですが、これがこの箱の局所座標です。 +</p> + +<h2>World space</h2> +<h2>大域空間</h2> +<p> + If we would import all our objects directly in the application they would probably all be somewhere positioned inside each other at the world's origin of <code>(0,0,0)</code> which is not what we want. We want to define a position for each object to position them inside a larger world. The coordinates in world space are exactly what they sound like: the coordinates of all your vertices relative to a (game) world. This is the coordinate space where you want your objects transformed to in such a way that they're all scattered around the place (preferably in a realistic fashion). The coordinates of your object are transformed from local to world space; this is accomplished with the <def>model</def> matrix. + 全ての物体を直接アプリケーションに読み込んだとすると、それらは世界の原点<code>(0, 0, 0)</code>において互いに重なり合ってしまいます。大きな世界の中でそれらの物体を適切な場所に配置したいものです。大域空間の座標はこの目的のために存在します。(ゲームの)世界における頂点の相対的な位置を表わすのがこの座標です。現実世界と同じように、この座標空間に向けて物体を座標変換して、それらを世界のあちこちに配置することができます。物体の局所座標が大域座標に変換されます。この変換を行うのが<def>モデル</def>行列です。 +</p> + +<p> + The model matrix is a transformation matrix that translates, scales and/or rotates your object to place it in the world at a location/orientation they belong to. Think of it as transforming a house by scaling it down (it was a bit too large in local space), translating it to a suburbia town and rotating it a bit to the left on the y-axis so that it neatly fits with the neighboring houses. You could think of the matrix in the previous chapter to position the container all over the scene as a sort of model matrix as well; we transformed the local coordinates of the container to some different place in the scene/world. + モデル行列は物体を平行移動し、拡大縮小し、あるいは回転することでアプリケーションの中の世界での位置や向きを決定する変換行列です。例えば局所座標の状態では大きすぎる家を縮小し、平行移動により郊外に移動させ、y軸に沿った回転により周囲の家にきちんと並ぶようにするような変換を考えることができます。また、前章で箱を移動させた行列も、ひとつのモデル行列だと考えることができます。箱の局所座標を変換して画面、あるいは世界の別の場所に置いたのです。 +</p> + +<h2>View space</h2> +<h2>視野空間</h2> +<p> + The view space is what people usually refer to as the <def>camera</def> of OpenGL (it is sometimes also known as <def>camera space</def> or <def>eye space</def>). The view space is the result of transforming your world-space coordinates to coordinates that are in front of the user's view. The view space is thus the space as seen from the camera's point of view. This is usually accomplished with a combination of translations and rotations to translate/rotate the scene so that certain items are transformed to the front of the camera. These combined transformations are generally stored inside a <def>view matrix</def> that transforms world coordinates to view space. In the next chapter we'll extensively discuss how to create such a view matrix to simulate a camera. + 視野空間はOpenGLの<def>カメラ</def>とも言われます。また、<def>カメラ空間</def>や、<def>eye space</def>とも呼ばれるものです。視野空間は大域空間の座標を変換してユーザーの目から見た座標にしたものです。つまり視野空間というのはカメラから見た空間です。この変換は通常平行移動と回転の組み合わせにより行われます。このような変換の組み合わせは一般的に<def>視野行列</def>に保存されます。この行列は大域座標系を視野空間に変換するものです。次章ではこの視野行列の作り方、カメラの動かし方を詳しく議論します。 +</p> + +<h2>Clip space</h2> +<h2>クリップ空間</h2> +<p> + At the end of each vertex shader run, OpenGL expects the coordinates to be within a specific range and any coordinate that falls outside this range is <def>clipped</def>. Coordinates that are clipped are discarded, so the remaining coordinates will end up as fragments visible on your screen. This is also where <def>clip space</def> gets its name from. + 各頂点シェーダーからの出力はある範囲に納まっていることが期待され、その範囲の外の座標は<def>切り落さ</def>れます。切り落された座標は捨てられ、のこった座標が最終的に画面上に表示されます。切り抜きは英語でクリップ(clip)というのでこれがこの空間の名前の由来です。 +</p> + +<p> + Because specifying all the visible coordinates to be within the range <code>-1.0</code> and <code>1.0</code> isn't really intuitive, we specify our own coordinate set to work in and convert those back to NDC as OpenGL expects them. + <code>-1.0</code>から<code>1.0</code>の範囲だけが表示されるというのはあまり直感的とは言えません。そのためもっと分かり易い座標系で作業した後、OpenGLが期待するNDCに変換するのです。 +</p> + +<p> + To transform vertex coordinates from view to clip-space we define a so called <def>projection matrix</def> that specifies a range of coordinates e.g. <code>-1000</code> and <code>1000</code> in each dimension. The projection matrix then transforms coordinates within this specified range to normalized device coordinates (<code>-1.0</code>, <code>1.0</code>). All coordinates outside this range will not be mapped between <code>-1.0</code> and <code>1.0</code> and therefore be clipped. With this range we specified in the projection matrix, a coordinate of (<code>1250</code>, <code>500</code>, <code>750</code>) would not be visible, since the <code>x</code> coordinate is out of range and thus gets converted to a coordinate higher than <code>1.0</code> in NDC and is therefore clipped. + 頂点座標を視野空間からクリップ空間に変換する為に、<def>射影行列</def>という行列を定義します。この行列は特定の範囲、例えば<code>-1000</code>から<code>1000</code>にある座標を正規化デバイス座標(<code>-1.0</code>から<code>1.0</code>)に変換します。この範囲外の座標は<code>-1.0</code>から<code>1.0</code>の中には入らず、切り落されます。射影行列でこのような範囲を指定した場合、<code>(1250, 500, 750)</code>というような座標は、<code>x</code>座標が範囲外にあり、NDCにおいて<code>1.0</code>より大きい値になるので、切り落され、表示されません。 +</p> + +<note> + Note that if only a part of a primitive e.g. a triangle is outside the <def>clipping volume</def> OpenGL will reconstruct the triangle as one or more triangles to fit inside the clipping range. + プリミティブの一部、例えば三角形の一部が<def>クリッピングボリューム</def>の外にある場合、OpenGLは三角形を増やしてNDCに納まるように変更します。 +</note> + +<p> + This <em>viewing box</em> a projection matrix creates is called a <def>frustum</def> and each coordinate that ends up inside this frustum will end up on the user's screen. The total process to convert coordinates within a specified range to NDC that can easily be mapped to 2D view-space coordinates is called <def>projection</def> since the projection matrix <def>projects</def> 3D coordinates to the easy-to-map-to-2D normalized device coordinates. + 射影行列が生成するこの<em>可視領域</em>は<def>視錐台</def>と呼ばれます。この視錐台の中にある座標が最終的に画面に表示されます。ある範囲の座標をNDCに落とし込み、2次元の視野空間に変換しやすくする一連の処理は<def>投影</def>と呼ばれます。射影行列により3次元の座標を2次元に変換し易い正規化デバイス座標に<def>投影</def>する処理だからです。 +</p> + +<p> + Once all the vertices are transformed to clip space a final operation called <def>perspective division</def> is performed where we divide the <code>x</code>, <code>y</code> and <code>z</code> components of the position vectors by the vector's homogeneous <code>w</code> component; perspective division is what transforms the 4D clip space coordinates to 3D normalized device coordinates. This step is performed automatically at the end of the vertex shader step. + 全ての頂点がクリップ空間に変換された後、<def>透視除算</def>という操作が最後に行われます。これは位置ベクトルの<code>x</code>、<code>y</code>、<code>z</code>座標を同次座標<code>w</code>で割り算する操作です。4次元のクリップ空間の座標を3次元のNDCに変換するものであるとも言えます。透視除算は頂点シェーダーの処理の最後に自動的に行われます。 +</p> + +<p> + It is after this stage where the resulting coordinates are mapped to screen coordinates (using the settings of <fun><function id='22'>glViewport</function></fun>) and turned into fragments. + この後、出力された座標が<fun><function id='22'>glViewport</function></fun>の設定を利用して画面の座標に射影されフラグメントに加工されます。 +</p> + +<p> + The projection matrix to transform view coordinates to clip coordinates usually takes two different forms, where each form defines its own unique frustum. We can either create an <def>orthographic</def> projection matrix or a <def>perspective</def> projection matrix. + 視野座標をクリップ座標に変換する射影行列には通常2つの種類があり、それぞれ特有の視錐台を作成します。それらは<def>平行投影</def>行列、<def>透視投影</def>行列と呼ばれるものです。 +</p> + +<h3>Orthographic projection</h3> +<h3>平行投影</h3> +<p> + An orthographic projection matrix defines a cube-like frustum box that defines the clipping space where each vertex outside this box is clipped. When creating an orthographic projection matrix we specify the width, height and length of the visible frustum. All the coordinates inside this frustum will end up within the NDC range after transformed by its matrix and thus won't be clipped. The frustum looks a bit like a container: + 平行投影行列は直方体の視錐台を作り、その外側を切り落します。平行投影行列の作成には視錐台の幅、高さ、長さを指定する必要があります。この視錐台の中の座標はこの行列による変換でNDCの範囲に納まり、切り落されません。この視錐台は箱のようなものです: +</p> + +<img src="/img/getting-started/orthographic_frustum.png" class="clean"/> + +<p> + The frustum defines the visible coordinates and is specified by a width, a height and a <def>near</def> and <def>far</def> plane. Any coordinate in front of the near plane is clipped and the same applies to coordinates behind the far plane. The orthographic frustum <strong>directly</strong> maps all coordinates inside the frustum to normalized device coordinates without any special side effects since it won't touch the <code>w</code> component of the transformed vector; if the <code>w</code> component remains equal to <code>1.0</code> perspective division won't change the coordinates. + 視錐台は可視領域を既定し、幅、高さ、そして<def>前端面(near plane)</def>と<def>後端面(far plane)</def>により決まります。前端面より手前のものや、後端面より後ろのものは切り落されます。平行投影による視錐台は<strong>直接</strong>NDCに射影され、変換されるベクトルの<code>w</code>座標には触れないのでほかの特別な効果はありません。もし<code>w</code>座標が<code>1.0</code>のままであれば透視除算によりなんの変化も起きないのです。 +</p> + +<p> + To create an orthographic projection matrix we make use of GLM's built-in function <code><function id='59'>glm::ortho</function></code>: + 平行投影行列を作成するにはGLMの組込み関数<code><function id='59'>glm::ortho</function></code>を利用します: +</p> + +<pre><code> +<function id='59'>glm::ortho</function>(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f); +</code></pre> + +<p> + The first two parameters specify the left and right coordinate of the frustum and the third and fourth parameter specify the bottom and top part of the frustum. With those 4 points we've defined the size of the near and far planes and the 5th and 6th parameter then define the distances between the near and far plane. This specific projection matrix transforms all coordinates between these <code>x</code>, <code>y</code> and <code>z</code> range values to normalized device coordinates. + 最初の2つの引数は視錐台の左端と右端の座標で、3つ目と4つ目は下端と上端の座標です。この4つの引数で前端面の大きさが決まり、5つ目と6つ目の引数で前端面と後端面の距離を決定します。この投影行列はここで指定した範囲にある<code>x</code>、<code>y</code>、<code>z</code>座標をNDCに変換します。 +</p> + +<p> + An orthographic projection matrix directly maps coordinates to the 2D plane that is your screen, but in reality a direct projection produces unrealistic results since the projection doesn't take <def>perspective</def> into account. That is something the <def>perspective projection</def> matrix fixes for us. + 平行投影行列は座標を直接2次元の画面に射影しますが、これはあまり現実的な映像にはなりません。<def>遠近法(perspective)</def>を考慮していない為です。この問題を解決するには<def>透視投影行列</def>を用います。 +</p> + +<h3>Perspective projection</h3> +<h3>透視投影</h3> +<p> + If you ever were to enjoy the graphics the <em>real life</em> has to offer you'll notice that objects that are farther away appear much smaller. This weird effect is something we call <def>perspective</def>. Perspective is especially noticeable when looking down the end of an infinite motorway or railway as seen in the following image: + <em>現実世界</em>が提供するグラフィックスを楽しんだことがあれば、遠くの物体は小さく見えることに気が付くでしょう。この奇妙な現象は<def>遠近法</def>によるものです。遠近法は下図のような、無限に続く道路や線路を見た時に特に目立ちます: +</p> + +<img src="/img/getting-started/perspective.png" class="clean"/> + +<p> + As you can see, due to perspective the lines seem to coincide at a far enough distance. This is exactly the effect perspective projection tries to mimic and it does so using a <def>perspective projection matrix</def>. The projection matrix maps a given frustum range to clip space, but also manipulates the <code>w</code> value of each vertex coordinate in such a way that the further away a vertex coordinate is from the viewer, the higher this <code>w</code> component becomes. Once the coordinates are transformed to clip space they are in the range <code>-w</code> to <code>w</code> (anything outside this range is clipped). OpenGL requires that the visible coordinates fall between the range <code>-1.0</code> and <code>1.0</code> as the final vertex shader output, thus once the coordinates are in clip space, perspective division is applied to the clip space coordinates: + 見ての通り、遠近法によって2つの線が無限のかなたで交差しているように見えます。これこそが<def>透視投影行列</def>を用いて透視投影が再現しようとしている現象です。この投影行列は視錐台をクリップ空間に変換しますが、それだけでなく各頂点の<code>w</code>の値も変更します。カメラから遠い頂点座標ほど、この<code>w</code>の値が大きくなるようにするのです。クリップ空間に変換されたらその座標は<code>-w</code>と<code>w</code>の間に落ち着きます(この外のものは切り落されます)。OpenGLにおいて表示されるべき座標は頂点シェーダーからの出力の段階で、<code>-1.0</code>と<code>1.0</code>の間になければいけないので、クリップ空間の中にある座標に対して透視除算が適応されます: + + \[ out = \begin{pmatrix} x /w \\ y / w \\ z / w \end{pmatrix} \] + + Each component of the vertex coordinate is divided by its <code>w</code> component giving smaller vertex coordinates the further away a vertex is from the viewer. This is another reason why the <code>w</code> component is important, since it helps us with perspective projection. The resulting coordinates are then in normalized device space. If you're interested to figure out how the orthographic and perspective projection matrices are actually calculated (and aren't too scared of the mathematics) I can recommend <a href="http://www.songho.ca/opengl/gl_projectionmatrix.html" target="_blank">this excellent article</a> by Songho. + 頂点座標の各要素は<code>w</code>要素の値で割り算されるので、遠くの頂点ほど小さい座標になります。このように、<code>w</code>要素は透視投影において重要な役割を果たします。以上の変換の結果出力される座標はNDSに納まります。もし平行投影行列や透視投影行列の導出に興味があるのであれば(そして数学を恐れないのであれば)Songhoによる<a href="http://www.songho.ca/opengl/gl_projectionmatrix.html" target="_blank">このすばらしい記事</a>をお薦めします。 +</p> + +<p> + A perspective projection matrix can be created in GLM as follows: + 透視投影行列はGLMにおいて以下のように作成できます: +</p> + +<pre><code> +glm::mat4 proj = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(45.0f), (float)width/(float)height, 0.1f, 100.0f); +</code></pre> + +<p> + What <code><function id='58'>glm::perspective</function></code> does is again create a large <em>frustum</em> that defines the visible space, anything outside the frustum will not end up in the clip space volume and will thus become clipped. A perspective frustum can be visualized as a non-uniformly shaped box from where each coordinate inside this box will be mapped to a point in clip space. An image of a perspective frustum is seen below: + <code><function id='58'>glm::perspective</function></code>が行うのは、可視領域である大きな<em>視錐台</em>を作ることです。この視錐台の外のものはクリップ空間に入らず、切り落とされてしまいます。透視投影による視錐台は歪んだ箱のようになります。この箱の中身がクリップ空間に射影されます。透視投影の視錐台は以下の画像のようなものです: +</p> + +<img src="/img/getting-started/perspective_frustum.png" class="clean"/> + + +<p> +Its first parameter defines the <def>fov</def> value, that stands for <def>field of view</def> and sets how large the viewspace is. For a realistic view it is usually set to 45 degrees, but for more doom-style results you could set it to a higher value. The second parameter sets the aspect ratio which is calculated by dividing the viewport's width by its height. The third and fourth parameter set the <em>near</em> and <em>far</em> plane of the frustum. We usually set the near distance to <code>0.1</code> and the far distance to <code>100.0</code>. All the vertices between the near and far plane and inside the frustum will be rendered. +1つ目の引数で<def>視野角(fov)</def>を定義します。これは<def>field of view</def>の省略で、見える範囲の広さを規定します。現実に近い値として通常45度を指定しますが、doomというゲームのような雰囲気にしたい場合もっと大きな値にすることもできます。2つ目の変数ではアスペクト比を指定します。アスペクト比は表示領域の幅を高さで割った値です。3つ目と4つ目の引数は視錐台の<em>前端面</em>と<em>後端面</em>を規定します。通常前端面までの距離には<code>0.1</code>を、後端面までの距離には<code>100.0</code>を指定します。視錐台においてこの前端面と後端面の間にある全ての頂点が描画されます。 +</p> + +<note> + Whenever the <em>near</em> value of your perspective matrix is set too high (like <code>10.0</code>), OpenGL will clip all coordinates close to the camera (between <code>0.0</code> and <code>10.0</code>), which can give a visual result you maybe have seen before in videogames where you could see through certain objects when moving uncomfortably close to them. + 透視投影行列において<em>前端面</em>までの距離としてもっと大きな値(例えば<code>10.0</code>)を与えると、OpenGLはそれよりカメラに近い座標(<code>0.0</code>と<code>10.0</code>の間のもの)は切り捨てられます。ゲームで遊んでいる時、何かに異常に近付くとその物体の中が見えた経験があるかと思いますが、そのような結果になります。 +</note> + +<p> + When using orthographic projection, each of the vertex coordinates are directly mapped to clip space without any fancy perspective division (it still does perspective division, but the <code>w</code> component is not manipulated (it stays <code>1</code>) and thus has no effect). Because the orthographic projection doesn't use perspective projection, objects farther away do not seem smaller, which produces a weird visual output. For this reason the orthographic projection is mainly used for 2D renderings and for some architectural or engineering applications where we'd rather not have vertices distorted by perspective. Applications like <em>Blender</em> that are used for 3D modeling sometimes use orthographic projection for modeling, because it more accurately depicts each object's dimensions. Below you'll see a comparison of both projection methods in Blender: + 平行投影では各頂点は直接クリップ空間に射影され、透視除算による面白い効果は得られません(透視除算は行なわれていますが、<code>w</code>が<code>1.0</code>のままなので影響がないのです)。平行投影は遠近法を適応していないので、遠くの物も小さくならず、奇妙な視覚効果が得られます。その為、平行投影は主に2次元の描画や、建築あるいは設計といった、遠近法による歪みが無い方がいい用途のアプリケーションで利用されます。<em>ブレンダー</em>のような3次元のモデリングに利用されるアプリケーションでも平行投影は時として利用されます。物体の寸法が正確に表示される為です。下図はブレンダーにおける両方の投影方法の比較です: +</p> + +<img src="/img/getting-started/perspective_orthographic.png" class="clean"/> + +<p> + You can see that with perspective projection, the vertices farther away appear much smaller, while in orthographic projection each vertex has the same distance to the user. + ご覧の様に透視投影では遠くの頂点は小さくなり、平行投影では各頂点のユーザーからの距離は同じままです。 +</p> + +<h2>Putting it all together</h2> +<h2>まとめ</h2> +<p> + We create a transformation matrix for each of the aforementioned steps: model, view and projection matrix. A vertex coordinate is then transformed to clip coordinates as follows: + 各段階に対する変換行列である、モデル変換行列、視野行列、投影行列を作成しました。これらを用いると、頂点の座標はクリップ座標に対して以下のように変換されます: + + \[ V_{clip} = M_{projection} \cdot M_{view} \cdot M_{model} \cdot V_{local} \] + +Note that the order of matrix multiplication is reversed (remember that we need to read matrix multiplication from right to left). The resulting vertex should then be assigned to <var>gl_Position</var> in the vertex shader and OpenGL will then automatically perform perspective division and clipping. +行列の積の順番が逆であることに注意してください。行列の積は右から左に読む必要があります。この積の結果が頂点シェーダーにおいて<var>gl_Position</var>に格納され、OpenGLが自動的に透視除算及び切り落としを行います。 +</p> + +<note> + <strong>And then?</strong><br/> + <strong>ほんで?</strong><br/> + The output of the vertex shader requires the coordinates to be in clip-space which is what we just did with the transformation matrices. OpenGL then performs <em>perspective division</em> on the <em>clip-space coordinates</em> to transform them to <em>normalized-device coordinates</em>. OpenGL then uses the parameters from <fun><function id='22'>glViewPort</function></fun> to map the normalized-device coordinates to <em>screen coordinates</em> where each coordinate corresponds to a point on your screen (in our case a 800x600 screen). This process is called the <em>viewport transform</em>. + 今行ったように、頂点シェーダーからの出力となる座標はクリップ空間に納まっていなければいけません。この後OpenGLはクリップ空間において<em>透視除算</em>を行い、<em>NDC</em>に変換します。そして<fun><function id='22'>glViewPort</function></fun>から値を取得し、NDCを<em>画面の座標系</em>に変換します。画面の座標は画面の各点に対応する座標で、今回の場合その大きさは800x600です。この処理は<em>ビューポート変換(viewport transform)</em>と呼ばれます。 +</note> + +<p> + This is a difficult topic to understand so if you're still not exactly sure about what each space is used for you don't have to worry. Below you'll see how we can actually put these coordinate spaces to good use and enough examples will follow in the upcoming chapters. + 本章の内容は難解で、各空間が何の為の物なのか理解し辛いかもしれませんが、心配は無用です。以下にこれらの座標空間の使い方を十分な例と共に示します。 +</p> + +<h1>Going 3D</h1> +<h1>3次元の世界へ</h1> +<p> + Now that we know how to transform 3D coordinates to 2D coordinates we can start rendering real 3D objects instead of the lame 2D plane we've been showing so far. + 3次元の座標を2次元に変換する方法が分かったので、ここまで見てきたようなつまらない平面の画像ではなく、現実世界のような3次元の物体の描画に進む準備が整いました。 +</p> + +<p> + To start drawing in 3D we'll first create a model matrix. The model matrix consists of translations, scaling and/or rotations we'd like to apply to <em>transform</em> all object's vertices to the global world space. Let's transform our plane a bit by rotating it on the x-axis so it looks like it's laying on the floor. The model matrix then looks like this: + 3次元のものを描画するにあたり、まずはモデル行列を作成しましょう。モデル行列は平行移動、拡大縮小、及び回転から構成されます。これらの変換は物体の各頂点を大域空間に<em>変換</em>する為の物です。とりあえず平面をx軸に沿って少し回転させ、床に敷かれているかのようにしてみましょう。このようなモデル行列は以下のようになります: +</p> + +<pre><code> +glm::mat4 model = glm::mat4(1.0f); +model = <function id='57'>glm::rotate</function>(model, <function id='63'>glm::radians</function>(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); +</code></pre> + +<p> +By multiplying the vertex coordinates with this model matrix we're transforming the vertex coordinates to world coordinates. Our plane that is slightly on the floor thus represents the plane in the global world. +頂点座標にモデル行列を掛けることで、頂点の座標を大域座標に変換しています。この操作により、この平面は大域的な世界での平面になりました。 + </p> + +<p> + Next we need to create a view matrix. We want to move slightly backwards in the scene so the object becomes visible (when in world space we're located at the origin <code>(0,0,0)</code>). To move around the scene, think about the following: + 次に視野行列を作成します。物体が見えるように少し後ろに下がりましょう(現状、大域空間の原点<code>(0, 0, 0)</code>に居ます)。この世界で移動する為に以下のように考えます: + <ul> + <li>To move a camera backwards, is the same as moving the entire scene forward.</li> + <li>カメラを手前に動かすのは世界全体を奥に動かすのと同じこと。</li> + </ul> + That is exactly what a view matrix does, we move the entire scene around inversed to where we want the camera to move.<br/> + Because we want to move backwards and since OpenGL is a right-handed system we have to move in the positive z-axis. We do this by translating the scene towards the negative z-axis. This gives the impression that we are moving backwards. + これはまさに視野行列が行うことです。カメラを動かしたい方向と逆向きに世界を動かします。<br/> + 今回はカメラを手前に動かしたいのですが、OpenGLの世界は右手の法則に従っているので、これはz軸の正の方向への移動になります。そのために世界をz軸の負の方向に移動させます。これにより自分自身が後ろに下ったように見えます。 +</p> + + +<note> + <strong>Right-handed system</strong> + <strong>右手の法則</strong> + <p> + By convention, OpenGL is a right-handed system. What this basically says is that the positive x-axis is to your right, the positive y-axis is up and the positive z-axis is backwards. Think of your screen being the center of the 3 axes and the positive z-axis going through your screen towards you. The axes are drawn as follows: + 慣例的にOpenGLは右手の法則を採用しています。これは座標空間の各軸が従う規則で、右側がx軸正の方向、上側がy軸正の方向、手前側がz軸正の方向になるようなものです。画面が座標空間の原点に位置し、z軸が手前を向いているとすると、以下のようになります: +</p> + <img src="/img/getting-started/coordinate_systems_right_handed.png" class="clean"/> + <p> + To understand why it's called right-handed do the following: + これが右手の法則と呼ばれるのは以下の理由からです: + <ul> + <li>Stretch your right-arm along the positive y-axis with your hand up top.</li> + <li>右手をy軸に沿うように上に向けます。</li> + <li>Let your thumb point to the right.</li> + <li>親指を右に向けます。</li> + <li>Let your pointing finger point up.</li> + <li>人差し指を上に向けます。</li> + <li>Now bend your middle finger downwards 90 degrees.</li> + <li>中指を90度折ります。</li> + </ul> + If you did things right, your thumb should point towards the positive x-axis, the pointing finger towards the positive y-axis and your middle finger towards the positive z-axis. If you were to do this with your left-arm you would see the z-axis is reversed. This is known as a left-handed system and is commonly used by DirectX. Note that in normalized device coordinates OpenGL actually uses a left-handed system (the projection matrix switches the handedness). + そうすると親指がx軸、人差し指がy軸、中指がz軸に対応するようになるはずです。左手で同じことをするとz軸は反対を向くであほう。これは左手の法則と呼ばれ、DirectXで利用されます。ただしNDCにおいて実際はOpenGLも左手の法則を利用しており、投影行列が反転しています。 + </p> +</note> + +<p> + We'll discuss how to move around the scene in more detail in the next chapter. For now the view matrix looks like this: +次章において空間上の移動を詳しく解説します。今のところ視野行列は以下のようになっています: +</p> + +<pre><code> +glm::mat4 view = glm::mat4(1.0f); +// note that we're translating the scene in the reverse direction of where we want to move +view = <function id='55'>glm::translate</function>(view, glm::vec3(0.0f, 0.0f, -3.0f)); +</code></pre> + +<p> + The last thing we need to define is the projection matrix. We want to use perspective projection for our scene so we'll declare the projection matrix like this: +最後に投影行列を定義します。遠近法を利用したいので、投影行列を以下のように宣言します: +</p> + +<pre><code> +glm::mat4 projection; +projection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); +</code></pre> + +<p> + Now that we created the transformation matrices we should pass them to our shaders. First let's declare the transformation matrices as uniforms in the vertex shader and multiply them with the vertex coordinates: + 以上でシェーダーに渡す変換行列を全て作成しました。それではそれらの変換行列を頂点シェーダーにおいてユニフォームとして定義し頂点座標と共に掛け合わせます: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +... +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + // note that we read the multiplication from right to left + gl_Position = projection * view * model * vec4(aPos, 1.0); + ... +} +</code></pre> + +<p> + We should also send the matrices to the shader (this is usually done each frame since transformation matrices tend to change a lot): + それから行列をシェーダーに送信しなければなりません。変換行列は通常頻繁に変更されるので行列の送信はフレーム毎に行います: +</p> + +<pre><code> +int modelLoc = <function id='45'>glGetUniformLocation</function>(ourShader.ID, "model"); +<function id='44'>glUniform</function>Matrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); +... // same for View Matrix and Projection Matrix +... // 視野行列、投影行列についても同様 +</code></pre> + +<p> + Now that our vertex coordinates are transformed via the model, view and projection matrix the final object should be: + これにて頂点座標がモデル、視野、投影行列により変換され、最終的な物体は以下のようになります: + + <ul> + <li>Tilted backwards to the floor. </li> + <li>傾いて床に置かれている。</li> + <li>A bit farther away from us.</li> + <li>カメラから少し離れている。</li> + <li>Be displayed with perspective (it should get smaller, the further its vertices are).</li> + <li>遠近法を用いて描画されている(遠くの頂点ほど小さい)。</li> + </ul> + + Let's check if the result actually does fulfill these requirements: + 本当にこのような結果になるか確かめましょう: +</p> + +<img src="/img/getting-started/coordinate_systems_result.png" class="clean"/> + +<p> + It does indeed look like the plane is a 3D plane that's resting at some imaginary floor. If you're not getting the same result, compare your code with the complete <a href="/code_viewer_gh.php?code=src/1.getting_started/6.1.coordinate_systems/coordinate_systems.cpp" target="_blank">source code</a>. + 確かに平面は3次元空間上で床に敷かれているように見えます。同じ結果にならなければ、自分のコードを完全な<a href="/code_viewer_gh.php?code=src/1.getting_started/6.1.coordinate_systems/coordinate_systems.cpp" target="_blank">ソースコード</a>と照らし合わせて下さい。 +</p> + +<h2>More 3D</h2> +<h2>もっと3次元</h2> +<p> + So far we've been working with a 2D plane, even in 3D space, so let's take the adventurous route and extend our 2D plane to a 3D cube. To render a cube we need a total of 36 vertices (6 faces * 2 triangles * 3 vertices each). 36 vertices are a lot to sum up so you can retrieve them from <a href="/code_viewer.php?code=getting-started/cube_vertices" target="_blank">here</a>. + ここまで空間が3次元になったとはいえ物体自体は2次元の平面でした。今度はもう少し冒険して、3次元の立方体を扱ってみましょう。立方体を描写するには頂点が36個も必要です(6つの面 * 2つの三角形 * 3つの頂点)。多すぎるので<a href="/code_viewer.php?code=getting-started/cube_vertices" target="_blank">ここ</a>からコピーして下さい。 +</p> + +<p> + For fun, we'll let the cube rotate over time: + さらに面白くするためにこの立方体を時間と共に回転させましょう: +</p> + +<pre><code> +model = <function id='57'>glm::rotate</function>(model, (float)<function id='47'>glfwGetTime</function>() * <function id='63'>glm::radians</function>(50.0f), glm::vec3(0.5f, 1.0f, 0.0f)); +</code></pre> + +<p> + And then we'll draw the cube using <fun><function id='1'>glDrawArrays</function></fun> (as we didn't specify indices), but this time with a count of 36 vertices. + それでは<fun><function id='1'>glDrawArrays</function></fun>により立方体を描写しましょう。今までは頂点の個数を指定しませんでしたが今回は36を指定します。 +</p> + +<pre class="cpp"><code> +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +</code></pre> + +<p> + You should get something similar to the following: + 以下のような結果が得られたでしょうか: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/coordinate_system_no_depth.mp4" type="video/mp4" /> + <img src="/img/getting-started/coordinate_systems_no_depth.png" class="clean"/> + </video> +</div> + + +<p> + It does resemble a cube slightly but something's off. Some sides of the cubes are being drawn over other sides of the cube. This happens because when OpenGL draws your cube triangle-by-triangle, fragment by fragment, it will overwrite any pixel color that may have already been drawn there before. Since OpenGL gives no guarantee on the order of triangles rendered (within the same draw call), some triangles are drawn on top of each other even though one should clearly be in front of the other. + 立方体のようにも見えますが、なにかが足りないようです。立方体のいくつかの面が他の面の後ろに描画されています。OpenGLはこの立方体を描画する際、各三角形を順番に、そしてフラグメントを順番に描き、先に描かれた部分を後に描かれた部分が上書きしてしまうのでこのような結果になります。単一の描画命令中に三角形をどの順番で描くのか、OpenGLは何も規定していないので、手前に描画されるべきものの上から別のものを描いてしまうことがあるのです。 +</p> + +<p> + Luckily, OpenGL stores depth information in a buffer called the <def>z-buffer</def> that allows OpenGL to decide when to draw over a pixel and when not to. Using the z-buffer we can configure OpenGL to do depth-testing. + 幸いOpenGLは深度の情報を<def>zバッファ</def>と呼ばれるバッファに保存しており、これを用いてピクセルを上書きすべきかどうか判断できます。zバッファを利用して、深度テストを行うようにOpenGLを設定できます。 +</p> + +<h3>Z-buffer</h3> +<h3>zバッファ</h3> +<p> + OpenGL stores all its depth information in a z-buffer, also known as a <def>depth buffer</def>. GLFW automatically creates such a buffer for you (just like it has a color-buffer that stores the colors of the output image). The depth is stored within each fragment (as the fragment's <code>z</code> value) and whenever the fragment wants to output its color, OpenGL compares its depth values with the z-buffer. If the current fragment is behind the other fragment it is discarded, otherwise overwritten. This process is called <def>depth testing</def> and is done automatically by OpenGL. + OpenGLは深度の情報をzバッファに格納しています。zバッファは<def>深度バッファ</def>とも呼ばれます。GLFWは自動的にこのバッファを作成します。これは出力となる色の情報を格納するカラーバッファと同様です。深度情報は<code>z</code>の値として各フラグメントに保存され、フラグメントが自身の色を出力しようとする際に、OpenGLがその深度値をzバッファと比較します。そのフラグメントが他のフラグメントの後ろに隠れていればそのフラグメントは破棄され、そうでなければ上書きされます。この処理を<def>深度テスト</def>と言い、OpenGLにより自動で行われます。 +</p> + +<p> + However, if we want to make sure OpenGL actually performs the depth testing we first need to tell OpenGL we want to enable depth testing; it is disabled by default. We can enable depth testing using <fun><function id='60'>glEnable</function></fun>. The <fun><function id='60'>glEnable</function></fun> and <fun>glDisable</fun> functions allow us to enable/disable certain functionality in OpenGL. That functionality is then enabled/disabled until another call is made to disable/enable it. Right now we want to enable depth testing by enabling <var>GL_DEPTH_TEST</var>: + しかしopenGLが確実に深度テストを行ってくれるようにするには、深度テストを有効化するように明記する必要があります。デフォルトでは無効になっている為です。<fun><function id='60'>glEnable</function></fun>を用いてこれを有効化できます。<fun><function id='60'>glEnable</function></fun>と<fun>glDisable</fun>はOpenGLの様々な機能を有効化、無効化するものです。ある機能を有効化、あるいは無効化すれば、次に明示的にそれを無効化、あるいは有効化するまでその機能は有効、あるいは無効です。今回深度テストを行いたいので、<var>GL_DEPTH_TEST</var>を有効化します: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +</code></pre> + +<p> + Since we're using a depth buffer we also want to clear the depth buffer before each render iteration (otherwise the depth information of the previous frame stays in the buffer). Just like clearing the color buffer, we can clear the depth buffer by specifying the <var>DEPTH_BUFFER_BIT</var> bit in the <fun><function id='10'>glClear</function></fun> function: + 深度バッファを利用するので、描画の度にそれを消去する必要があります(そうしないと前のフレームがこのバッファに残ったままになります)。カラーバッファと同様に<fun><function id='10'>glClear</function></fun>に<var>DEPTH_BUFFER_BIT</var>を渡すことで削除できます。 +</p> + +<pre><code> +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +</code></pre> + +<p> + Let's re-run our program and see if OpenGL now performs depth testing: + それでは再びプログラムを実行し、深度テストが行われているか確認しましょう: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/coordinate_system_depth.mp4" type="video/mp4" /> + <img src="/img/getting-started/coordinate_systems_with_depth.png" class="clean"/> + </video> +</div> + + +<p> + There we go! A fully textured cube with proper depth testing that rotates over time. Check the source code <a href="/code_viewer_gh.php?code=src/1.getting_started/6.2.coordinate_systems_depth/coordinate_systems_depth.cpp" target="_blank">here</a>. + ええやん。前面にテクスチャの施された立方体が、適切な深度テストが行われた上で時間と共に回転するようになりました。<a href="/code_viewer_gh.php?code=src/1.getting_started/6.2.coordinate_systems_depth/coordinate_systems_depth.cpp" target="_blank">ここ</a>からソースコードを確認して下さい。 +</p> + + + +<h3>More cubes!</h3> +<h3>もっと沢山の立方体</h3> +<p> + Say we wanted to display 10 of our cubes on screen. Each cube will look the same but will only differ in where it's located in the world with each a different rotation. The graphical layout of the cube is already defined so we don't have to change our buffers or attribute arrays when rendering more objects. The only thing we have to change for each object is its model matrix where we transform the cubes into the world. + 今度は10個の立方体を表示してみましょう。同じ立方体を場所と角度を変えて描画しましょう。立方体の形やテクスチャは既に定義しているので、複数の立方体を表示させるからといってバッファや属性配列を変える必要はありません。必要なのはモデル行列を各物体毎に変え、それぞれを大域空間の別の場所に座標変換することです。 +</p> + +<p> + First, let's define a translation vector for each cube that specifies its position in world space. We'll define 10 cube positions in a <code>glm::vec3</code> array: + まずは大域空間中の各立方体の場所を示す平行移動ベクトルを定義しましょう。<code>glm::vec3</code>の配列として、10個の立方体の位置を指定します: +</p> + +<pre><code> +glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) +}; +</code></pre> + +<p> + Now, within the render loop we want to call <fun><function id='1'>glDrawArrays</function></fun> 10 times, but this time send a different model matrix to the vertex shader each time before we send out the draw call. We will create a small loop within the render loop that renders our object 10 times with a different model matrix each time. Note that we also add a small unique rotation to each container. + 次に描画ループの中で<fun><function id='1'>glDrawArrays</function></fun>を10回呼びます。ただし今回は描画命令の前にそれぞれ別のモデル行列を頂点シェーダーに送信します。描画ループの中で小さなループを作成し、10個の物体をそれぞれ別のモデル行列により描写します。ついでにそれぞれ別の角度だけ回転を加えましょう: +</p> + +<pre><code> +<function id='27'>glBindVertexArray</function>(VAO); +for(unsigned int i = 0; i &lt; 10; i++) +{ + glm::mat4 model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, cubePositions[i]); + float angle = 20.0f * i; + model = <function id='57'>glm::rotate</function>(model, <function id='63'>glm::radians</function>(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + ourShader.setMat4("model", model); + + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +} +</code></pre> + +<p> + This snippet of code will update the model matrix each time a new cube is drawn and do this 10 times in total. Right now we should be looking into a world filled with 10 oddly rotated cubes: + このコードにより新しい立方体が描画される度にモデル行列が更新されます。今回は少し回転した立方体が10個ちりばめられた世界が描画されるはずです。 +</p> + +<img src="/img/getting-started/coordinate_systems_multiple_objects.png" class="clean"/> + +<p> + Perfect! It looks like our container found some like-minded friends. If you're stuck see if you can compare your code with the <a href="/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/coordinate_systems_multiple.cpp" target="_blank">source code</a>. + 完の璧です。箱が気の合う仲間を見付けたようです。どこかで詰まってしまったのであれば<a href="/code_viewer_gh.php?code=src/1.getting_started/6.3.coordinate_systems_multiple/coordinate_systems_multiple.cpp" target="_blank">ソースコード</a>と比較してください。 +</p> + +<h2>Exercises</h2> +<h2>演習問題</h2> +<ul> + <li>Try experimenting with the <code>FoV</code> and <code>aspect-ratio</code> parameters of GLM's <code>projection</code> function. See if you can figure out how those affect the perspective frustum.</li> + <li>GLMの<code>projection</code>関数の<code>FoV</code>と<code>aspect-ratio</code>を色々変化させて何が起こるか実験して下さい。透視投影の視錐台にどう影響するでしょうか。</li> + <li>Play with the view matrix by translating in several directions and see how the scene changes. Think of the view matrix as a camera object.</li> + <li>視野行列を色々な方向に平行移動し、画面がどう変化するか実験して下さい。視野行列をカメラオブジェクトだと考えましょう。</li> + <li>Try to make every 3rd container (including the 1st) rotate over time, while leaving the other containers static using just the model matrix: <a href="/code_viewer_gh.php?code=src/1.getting_started/6.4.coordinate_systems_exercise3/coordinate_systems_exercise3.cpp" target="_blank">solution</a>.</li> + <li>モデル行列だけを利用し、1つ目の箱を含み3つおきの箱を時間と共に回転させて下さい。他の箱は固定したままにして下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/6.4.coordinate_systems_exercise3/coordinate_systems_exercise3.cpp" target="_blank">解答</a>。</li> +</ul> + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Creating-a-window.html b/pub/Getting-started/Creating-a-window.html @@ -0,0 +1,607 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Creating a window</h1> + <h1 id="content-title">ウィンドウの作成</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Creating-a-window</h1> +<p> + The first thing we need to do before we start creating stunning graphics is to create an OpenGL context and an application window to draw in. However, those operations are specific per operating system and OpenGL purposefully tries to abstract itself from these operations. This means we have to create a window, define a context, and handle user input all by ourselves. +</p> +<p> +魅力的な映像を作成するにあたり、OpenGLのコンテクストとアプリケーションのウィンドウを作成する必要があります。しかしこの作業はOSによってまちまちであり、OpenGLはこういった作業から自身をあえて抽象化しようとしています。つまりウィンドウの作成やコンテクストの定義、あるいはユーザーからの入力の処理といった作業を自分自身でおこなわなければなりません。 +</p> + +<p> + Luckily, there are quite a few libraries out there that provide the functionality we seek, some specifically aimed at OpenGL. Those libraries save us all the operation-system specific work and give us a window and an OpenGL context to render in. Some of the more popular libraries are GLUT, SDL, SFML and GLFW. On LearnOpenGL we will be using <strong>GLFW</strong>. Feel free to use any of the other libraries, the setup for most is similar to GLFW's setup. +</p> +<p> +ありがたいことに、我々の目的にあった機能を提供するライブラリがたくさんあり、なかにはOpenGLに特化したものもあります。こういったライブラリを利用することで、OSごとに違うコードを用意しなくてもウィンドウやコンテクストを作成することが可能です。有名なライブラリにはGLUT、SDL、SFML、そしてGLFW等があります。本書では<strong>GLFW</strong>を利用します。しかし他のどのライブラリを使ってもかまいません。セットアップ方法はだいたい同じです。 +</p> + +<h2>GLFW</h2> +<p> + GLFW is a library, written in C, specifically targeted at OpenGL. GLFW gives us the bare necessities required for rendering goodies to the screen. It allows us to create an OpenGL context, define window parameters, and handle user input, which is plenty enough for our purposes. +</p> +<p> +GLFWはOpenGLに特化したC言語製のライブラリです。GLFWはスクリーンに素敵なものを描画するために必要な最低限のものを提供します。GLFWにより、コンテクストの作成、ウィンドウに係る変数の定義、ユーザーからの入力の処理が可能になり、われわれの目的には十分です。 +</p> + +<p> + The focus of this and the next chapter is to get GLFW up and running, making sure it properly creates an OpenGL context and that it displays a simple window for us to mess around in. This chapter takes a step-by-step approach in retrieving, building and linking the GLFW library. We'll use Microsoft Visual Studio 2019 IDE as of this writing (note that the process is the same on the more recent visual studio versions). If you're not using Visual Studio (or an older version) don't worry, the process will be similar on most other IDEs. +</p> +<p> +この章と次の章ではGLFWを動作させ、コンテクストの作成や簡単なウィンドウの表示が適切にできるか確認します。この章ではGLFWライブラリをダウンロード、コンパイルしリンクする方法を順番に見ていきます。MicrosoftのVisual Studio 2019 IDEを利用します(もっと新しいバージョンのVisual Studioでもやりかたは同じです)。あなたがVisual Studioの古いバージョンや他のIDEを使う場合でも、だいたい同じ方法で動作します。 +</p> + +<h2>Building GLFW</h2> +<h2>GLFWのビルド</h2> +<p> + GLFW can be obtained from their webpage's <a href="http://www.glfw.org/download.html" target="_blank">download</a> page. GLFW already has pre-compiled binaries and header files for Visual Studio 2012 up to 2019, but for completeness' sake we will compile GLFW ourselves from the source code. This is to give you a feel for the process of compiling open-source libraries yourself as not every library will have pre-compiled binaries available. So let's download the <em>Source package</em>. +</p> +<p> +GLFWはこの<a href="http://www.glfw.org/download.html" target="_blank">ダウンドード</a>ページから入手できます。GLFWはVisual Studio 2012から2019ではあらかじめコンパイルされたバイナリとヘッダーファイルも利用できますが、完全性のために自分達でコンパイルすることにします。オープンソースのライブラリによってはバイナリが用意されていないことがあるので、ライブラリをコンパイルすることに慣れておいたほうがいいからです。それでは<em>ソースパッケージ</em>をダウンロードしましょう。 +</p> + +<warning> + We'll be building all libraries as 64-bit binaries so make sure to get the 64-bit binaries if you're using their pre-compiled binaries. +</warning> +<warning> +ここではすべてのライブラリを64bit用にコンパイルします。コンパイル済のバイナリーをダウンロードする場合、64bit用のものであることを確認してください。 +</warning> + +<p> + Once you've downloaded the source package, extract it and open its content. We are only interested in a few items: +</p> +<p> +ダウンロードしたソースパッケージを解凍します。我々が利用するのは以下のものだけです: +</p> + <ul> + <li>The resulting library from compilation.</li> + <li>The <strong>include</strong> folder.</li> + </ul> + <ul> + <li>ライブラリをコンパイルしたもの。</li> + <li><strong>include</strong>フォルダ。</li> + </ul> + +<p> + Compiling the library from the source code guarantees that the resulting library is perfectly tailored for your CPU/OS, a luxury pre-compiled binaries don't always provide (sometimes, pre-compiled binaries are not available for your system). The problem with providing source code to the open world however is that not everyone uses the same IDE or build system for developing their application, which means the project/solution files provided may not be compatible with other people's setup. + So people then have to setup their own project/solution with the given .c/.cpp and .h/.hpp files, which is cumbersome. Exactly for those reasons there is a tool called CMake. +</p> +<p> +ライブラリをソースからビルドした場合、あなたのCPUやOSに完全に適合することが保証されます。これはコンパイル済のバイナリでは味わえない贅沢です(場合によってはあなたのシステムで動作するバイナリが提供されていないこともあります)。オープンソースの世界でソースコードを提供することの問題は、すべてのユーザーがアプリケーションを開発するうえでおなじIDEやビルドシステムを利用していないことです。誰かのプロジェクトやソリューションが他の人の環境で動作しない可能性があるということです。 +</p> + +<h3>CMake</h3> +<p> + CMake is a tool that can generate project/solution files of the user's choice (e.g. Visual Studio, Code::Blocks, Eclipse) from a collection of source code files using pre-defined CMake scripts. This allows us to generate a Visual Studio 2019 project file from GLFW's source package which we can use to compile the library. First we need to download and install CMake which can be downloaded on their <a href="http://www.cmake.org/cmake/resources/software.html" target="_blank">download</a> page. +</p> +<p> +CMakeはソースコードとあらかじめ用意しておいたCMakeのスクリプトから、任意のIDE(例えばVisual Studio、Code::Blocks、Eclipse等)のプロジェクトやソリューションのファイルを作成するツールです。これを使えば、GLFWのソースパッケージからVisual Studio 2019のプロジェクトファイルを作成することができ、さらにプロジェクトファイルをコンパイルしてライブラリを作成することができます。まずはCMakeを<a href="http://www.cmake.org/cmake/resources/software.html" target="_blank">ここ</a>からダウンロードしてインストールしましょう。 +</p> + +<p> + Once CMake is installed you can choose to run CMake from the command line or through their GUI. Since we're not trying to overcomplicate things we're going to use the GUI. CMake requires a source code folder and a destination folder for the binaries. For the source code folder we're going to choose the root folder of the downloaded GLFW source package and for the build folder we're creating a new directory <em>build</em> and then select that directory. +</p> +<p> +CMakeはコマンドラインからでもGUIを通してでも、好きな方法で利用できます。込み入った話をさけるためここではGUIを利用します。CMakeを利用するには、ソースコードのフォルダとコンパイルしてできあがったバイナリ用のフォルダのふたつが必要です。ソースコードのフォルダとしてGLFWをダウンロードしたフォルダを選び、バイナリ用には<em>build</em>というフォルダを新しく作成することにしましょう。 +</p> + +<img src="/img/getting-started/cmake.png" width="800px" alt="Image of CMake's logo"/> + +<p> + Once the source and destination folders have been set, click the <code>Configure</code> button so CMake can read the required settings and the source code. We then have to choose the generator for the project and since we're using Visual Studio 2019 we will choose the <code>Visual Studio 16</code> option (Visual Studio 2019 is also known as Visual Studio 16). CMake will then display the possible build options to configure the resulting library. We can leave them to their default values and click <code>Configure</code> again to store the settings. Once the settings have been set, we click <code>Generate</code> and the resulting project files will be generated in your <code>build</code> folder. +</p> +<p> +ソースコード用およびバイナリ用のフォルダを設定したら、<code>Configure</code>ボタンをクリックしてCMakeに設定とソースコードを読み込みます。次にジェネレータの項目を設定します。今回利用するのはVisual Studio 2019なので<code>Visual Studio 16</code>を選んでください(Visual Studio 2019はVisual Studio 16とも呼ばれます)。そうすればCMakeが選択可能なビルドオプションを表示します。ここではデフォルトのままにしておいてかまいません。設定を保存するために再度<code>Configure</code>をクリックしてください。設定完了後、<code>Generate</code>をクリックすれば<code>build</code>フォルダにプロジェクトファイルが作成されます。 +</p> + +<h3>Compilation</h3> +<h3>コンパイル</h3> +<p> + In the <code>build</code> folder a file named <code>GLFW.sln</code> can now be found and we open it with Visual Studio 2019. Since CMake generated a project file that already contains the proper configuration settings we only have to build the solution. CMake should've automatically configured the solution so it compiles to a 64-bit library; now hit build solution. This will give us a compiled library file that can be found in <code>build/src/Debug</code> named <code>glfw3.lib</code>.</p> +<p> +<code>build</code>フォルダに<code>GLFW.sln</code>というファイルがあるはずですので、Visual Studio 2019で開いてください。CMakeが作成したプロジェクトファイルには必要な設定がすべて含まれているので、あとはソリューションをビルドすればいいだけです。64bitのライブラリをコンパイルするようにCMakeが自動的に設定してくれています。build solutionを押しましょう。さすればライブラリはコンパイルされ、<code>build/src/Debug</code>というフォルダに<code>glfw3.lib</code>なるファイルが出現するでしょう。 +</p> + +<p> + Once we generated the library we need to make sure the IDE knows where to find the library and the include files for our OpenGL program. There are two common approaches in doing this: + <ol> + <li> We find the <code>/lib</code> and <code>/include</code> folders of the IDE/compiler and add the content of GLFW's <code>include</code> folder to the IDE's <code>/include</code> folder and similarly add <code>glfw3.lib</code> to the IDE's <code>/lib</code> folder. This works, but it's is not the recommended approach. It's hard to keep track of your library and include files and a new installation of your IDE/compiler results in you having to do this process all over again. </li> + <li> + Another approach (and recommended) is to create a new set of directories at a location of your choice that contains all the header files/libraries from third party libraries to which you can refer to from your IDE/compiler. You could, for instance, create a single folder that contains a <code>Libs</code> and <code>Include</code> folder where we store all our library and header files respectively for OpenGL projects. Now all the third party libraries are organized within a single location (that can be shared across multiple computers). The requirement is, however, that each time we create a new project we have to tell the IDE where to find those directories. + </li> + </ol> + Once the required files are stored at a location of your choice, we can start creating our first OpenGL GLFW project. +</p> +<p> +ライブラリをコンパイルしたら、IDEにその在り処を伝えねばなりません。それには二通りの方法があります: + <ol> + <li>IDEやコンパイラの<code>/lib</code>と<code>/include</code>フォルダを見つけ、そこに<code>glfw3.lib</code>とGLFWの<code>include</code>をそれぞれ追加する方法。これは機能はしますがおすすめはできません。追加したライブラリやインクルードファイルを覚えておくのが大変ですし、新しいIDEやコンパイラをインストールするたびに同じ作業を繰り返すはめになります。</li> + <li>IDEやコンパイラが見付けられる場所に新しいフォルダを作成し、その中にサードパーティー製のライブラリに関するすべてのヘッダーファイルとライブラリファイルを集める方法。こちらの方法がおすすめです。例えば<code>Libs</code>と<code>Include</code>というフォルダを作り、OpenGLのプロジェクトで使うライブラリとヘッダーファイルをすべてここにおいておきます。こうすればサードパーティー製のライブラリが一つの場所にまとめられます(この場所を複数のコンピュータで共有することも可能です)。こちらの方法では、新しいプロジェクトを作成するたびにIDEにライブラリのある場所を教える必要があります。 + </li> + </ol> + 必要なファイルを参照できる場所に配置できれば、いよいよGLFWを利用したOpenGLのプロジェクトを作成できます。 +</p> + +<h2>Our first project</h2> +<h2>最初のプロジェクト</h2> +<p> + First, let's open up Visual Studio and create a new project. Choose C++ if multiple options are given and take the <code>Empty Project</code> (don't forget to give your project a suitable name). Since we're going to be doing everything in 64-bit and the project defaults to 32-bit, we'll need to change the dropdown at the top next to Debug from x86 to x64: +</p> +<p> +まずはVisual Studioを立ちあげて新しいプロジェクトを作成しましょう。選択肢があればC++を選び、<code>Empty Project</code>を作成し、プロジェクトに適切な名前を付けてください。デフォルトは32bitになっていますが本書では64bitで開発しますので、いちばん上にあるDebugの隣のドロップダウンをx64からx86に変更してください: +</p> + +<img src="/img/getting-started/x64.png" alt="Image of how to switch from x86 to x64"/>。 + +<p> + Once that's done, we now have a workspace to create our very first OpenGL application! +これでOpenGLのアプリケーションを作る下準備は完了です。 +</p> + + +<h2>Linking</h2> +<h2>リンク</h2> +<p> + In order for the project to use GLFW we need to <def>link</def> the library with our project. This can be done by specifying we want to use <code>glfw3.lib</code> in the linker settings, but our project does not yet know where to find <code>glfw3.lib</code> since we store our third party libraries in a different directory. We thus need to add this directory to the project first. +プロジェクトがGLFWを利用するにはライブラリをプロジェクトに<def>リンク</def>する必要があります。これには<code>glfw3.lib</code>を使うようリンカを設定すればいいのですが、先程サードパーティー製のライブラリを別のディレクトリに入れたので、プロジェクトはどこに<code>glfw3.lib</code>があるのかを知りません。プロジェクトにこのディレクトリの場所を追加する必要があります。 +</p> + +<p> + We can tell the IDE to take this directory into account when it needs to look for library and include files. Right-click the project name in the solution explorer and then go to <code>VC++ Directories</code> as seen in the image below: +ライブラリやインクルードファイルが必要なときに先程作ったのディレクトリも探すようにIDEに伝えます。ソリューションエクスプローラにおいてプロジェクト名を右クリックし<code>VC++ Directories</code>を選択してください: +</p> + +<img src="/img/getting-started/vc_directories.png" width="600px" alt="Image of Visual Studio's VC++ Directories configuration"/>。 + +<p> + From there on out you can add your own directories to let the project know where to search. This can be done by manually inserting it into the text or clicking the appropriate location string and selecting the <code>&lt;Edit..&gt;</code> option. Do this for both the <code>Library Directories</code> and <code>Include Directories</code>: + ここからわれわれのディレクトリをプロジェクトに伝えることができます。直接テキストに入力してもいいですし、しかる場所をクリックして<code>&lt;Edit..&gt;</code>を選択してもかまいません。<code>ライブラリのディレクトリ</code>と<code>インクルードディレクトリ</code>の両方を登録してください: +</p> + +<img src="/img/getting-started/include_directories.png" width="600px" alt="Image of Visual Studio's Include Directories configuration"/>。 + +<p> + Here you can add as many extra directories as you'd like and from that point on the IDE will also search those directorie when searching for library and header files. As soon as your <code>Include</code> folder from GLFW is included, you will be able to find all the header files for GLFW by including <code>&lt;GLFW/..&gt;</code>. The same applies for the library directories. +ここですきなだけディレクトリを追加でき、IDEはライブラリやヘッダファイルが必要なときに追加したディレクトリも検索してくれます。GLFWの<code>インクルード</code>フォルダを追加すれば<code>&lt;GLFW/..&gt;</code>をインクルードすることでGLFWのヘッダーファイルを利用することができます。 +</p> + +<p> + Since VS can now find all the required files we can finally link GLFW to the project by going to the <code>Linker</code> tab and <code>Input</code>: + 必要なファイルをVisual Studioが見つけられるようになったので、ようやくGLFWをプロジェクトにリンクできます。<code>Linker</code>タブの中の<code>Input</code>を選択してください: +</p> + +<img src="/img/getting-started/linker_input.png" width="600px" alt="Image of Visual Studio's link configuration"/>。 + +<p> + To then link to a library you'd have to specify the name of the library to the linker. Since the library name is <code>glfw3.lib</code>, we add that to the <code>Additional Dependencies</code> field (either manually or using the <code>&lt;Edit..&gt;</code> option) and from that point on GLFW will be linked when we compile. In addition to GLFW we should also add a link entry to the OpenGL library, but this may differ per operating system: +ライブラリをリンクするために、こんどはリンカにライブラリ名を伝える必要があります。ライブラリ名は<code>glfw3.lib</code>なので、この名前を<code>Additional Dependencies</code>に追加します(手入力でも<code>&lt;Edit..&gt;</code>からでもかまいません)。これでコンパイル時にGLFWがリンクされるようになりました。GLFWに加えて、OpenGLのライブラリもリンクしなければなりませんが、これはOSによってすこし違います: +</p> + +<h3>OpenGL library on Windows</h3> +<h3>WindowsにおけるOpenGLライブラリ</h3> +<p> + If you're on Windows the OpenGL library <code>opengl32.lib</code> comes with the Microsoft SDK, which is installed by default when you install Visual Studio. Since this chapter uses the VS compiler and is on windows we add <code>opengl32.lib</code> to the linker settings. Note that the 64-bit equivalent of the OpenGL library is called <code>opengl32.lib</code>, just like the 32-bit equivalent, which is a bit of an unfortunate name. +WindowsユーザーはいますぐLinuxかBSDにのりかえてください。OpenGLのライブラリは<code>opengl32.lib</code>とよばれ、Microsoft SDKに付属しています。Microsoft SDKはVisual Studioに標準で搭載されています。この章ではVisual Studioのコンパイラを利用し、Windows上で作業を行なっているのでリンカの設定に<code>opengl32.lib</code>を追加しましょう。まぎらわしいことに64bit用のライブラリは32bitライブラリであるかのようなへんてこな名前をしていますので注意してください。 +</p> + +<h3>OpenGL library on Linux</h3> +<h3>LinuxにおけるOpenGLライブラリ</h3> +<p> + On Linux systems you need to link to the <code>libGL.so</code> library by adding <code>-lGL</code> to your linker settings. If you can't find the library you probably need to install any of the Mesa, NVidia or AMD dev packages. +Linuxでは<code>libGL.so</code>をリンクするためにリンカの設定に<code>-lGL</code>を追加してください。ライブラリが見つからない場合、Mesa、NVidiaあるいはAMDの開発用パッケージをインストールしてください。 +</p> + +<p> + Then, once you've added both the GLFW and OpenGL library to the linker settings you can include the header files for GLFW as follows: +GLFWとOpenGLライブラリをリンカの設定に追加したら、GLFWのヘッダーファイルをインクルードできます: +</p> + +<pre><code> +#include &lt;GLFW\glfw3.h&gt; +</code></pre>。 + +<note> + For Linux users compiling with GCC, the following command line options may help you compile the project: <code>-lglfw3 -lGL -lX11 -lpthread -lXrandr -lXi -ldl</code>. Not correctly linking the corresponding libraries will generate many <em>undefined reference</em> errors. +GCCを利用しているLinuxユーザーは以下のコマンドラインオプションが便利です: <code>-lglfw3 -lGL -lX11 -lpthread -lXrandr -lXi -ldl</code>。リンクがうまくいっていないと、大量の<em>undefined reference</em>エラーがでます。 +</note> + +<p> + This concludes the setup and configuration of GLFW. + 以上でGLFWの設定は完了です。 +</p> + +<h2>GLAD</h2> +<p> + We're still not quite there yet, since there is one other thing we still need to do. Because OpenGL is only really a standard/specification it is up to the driver manufacturer to implement the specification to a driver that the specific graphics card supports. Since there are many different versions of OpenGL drivers, the location of most of its functions is not known at compile-time and needs to be queried at run-time. It is then the task of the developer to retrieve the location of the functions he/she needs and store them in function pointers for later use. Retrieving those locations is <a href="https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions" target="_blank">OS-specific</a>. In Windows it looks something like this: +まだです。もうひとつだけやり残したことがあります。OpenGLはただの規格であり仕様であるため、各グラフィックカードがサポートするドライバにおいて、その仕様をどのように実装するかはドライバの作成者に任されています。OpenGLのドライバはたくさんあるので、関数の場所は基本的にコンパイル時にはわからず、実行時に探し出す必要があります。そのため必要な関数の場所を割り出し、その場所を関数へのポインタとして保存するのは開発者の仕事なのです。この仕事は<a href="https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions" target="_blank">OSによってまちまち</a>です。Windowsでは以下のようにします: +</p> + +<pre><code> +// define the function's prototype +// 関数のプロトタイプ宣言 +typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*); +// find the function and assign it to a function pointer +// 関数を見つけ、ポインタを割り当て +GL_GENBUFFERS <function id='12'>glGenBuffers</function> = (GL_GENBUFFERS)wglGetProcAddress("<function id='12'>glGenBuffers</function>"); +// function can now be called as normal +// 上の作業により関数は普通に呼び出せる +unsigned int buffer; +<function id='12'>glGenBuffers</function>(1, &buffer); +</code></pre> + + <p> + As you can see the code looks complex and it's a cumbersome process to do this for each function you may need that is not yet declared. Thankfully, there are libraries for this purpose as well where <strong>GLAD</strong> is a popular and up-to-date library. +ご覧のように必要な関数すべてに対していちいちこれを行うのはかったるい作業です。ありがたいことにこの仕事を肩代りしてくれるライブラリがあります。<strong>GLAD</strong>はそのようなライブラリのなかでも人気があり常に更新されています。 + </p> + +<h3>Setting up GLAD</h3> +<h3>GLADの設定</h3> + <p> + GLAD is an <a href="https://github.com/Dav1dde/glad" target="_blank">open source</a> library that manages all that cumbersome work we talked about. GLAD has a slightly different configuration setup than most common open source libraries. GLAD uses a <a href="http://glad.dav1d.de/" target="_blank">web service</a> where we can tell GLAD for which version of OpenGL we'd like to define and load all relevant OpenGL functions according to that version. +GLADは<a href="https://github.com/Dav1dde/glad" target="_blank">オープンソース</a>のライブラリで、上記のような面倒な作業を行ってくれるものです。GLADは他の多くのオープンソースなライブラリとは少し違った方法でセットアップします。GLADは<a href="http://glad.dav1d.de/" target="_blank">ウェブサービス</a>を利用しています。利用したいOpenGLのバージョンをGLADに伝えれば、そのバージョンの関数を全てロードできるようになります。 + </p> + +<p> + Go to the GLAD <a href="http://glad.dav1d.de/" target="_blank">web service</a>, make sure the language is set to C++, and in the API section select an OpenGL version of at least 3.3 (which is what we'll be using; higher versions are fine as well). Also make sure the profile is set to <em>Core</em> and that the <em>Generate a loader</em> option is ticked. Ignore the extensions (for now) and click <em>Generate</em> to produce the resulting library files. +GLADの<a href="http://glad.dav1d.de/" target="_blank">ウェブサービス</a>に行き、言語がC++になっていることを確認し、APIの部分でOpenGLのバージョンから3.3以上のものを選択してください(3.3は本書で使うバージョンですが、もっと新しいバージョンでもかまいません)。また、profileが<em>Core</em>であることおよび、<em>Generate a loader</em>オプションにチェックが入っていることも確認してください。Extentionはとりあえず無視してかまいませんので、<em>Generate</em>をクリックしてライブラリを作成してください。 +</p> + +<p> + GLAD by now should have provided you a zip file containing two include folders, and a single <code>glad.c</code> file. Copy both include folders (<code>glad</code> and <code>KHR</code>) into your include(s) directoy (or add an extra item pointing to these folders), and add the <code>glad.c</code> file to your project. + そうすれば二つのインクルードフォルダとひとつの<code>glad.c</code>ファイルが入ったzipファイルがダウンロードできます。これらのフォルダー(<code>glad</code>と<code>KHR</code>)をインクルードディレクトリへコピー(またはこれらのフォルダを参照できるように)し、<code>glad.c</code>ファイルをプロジェクトに追加してください。 +</p> + +<p> + After the previous steps, you should be able to add the following include directive above your file: + 以上により、ファイルの冒頭にインクルードディレクティブを追加できます: +</p> + +<pre><code> +#include &lt;glad/glad.h&gt; +</code></pre> + +<p> + Hitting the compile button shouldn't give you any errors, at which point we're set to go for the <a href="https://learnopengl.com/Getting-started/Hello-Window" target="_blank">next</a> chapter where we'll discuss how we can actually use GLFW and GLAD to configure an OpenGL context and spawn a window. Be sure to check that all your include and library directories are correct and that the library names in the linker settings match the corresponding libraries. + コンパイルボタンを押してエラーがでなければ、<a href="https://learnopengl.com/Getting-started/Hello-Window" target="_blank">次の</a>章に進むことができます。次の章では実際にGLFWとGLADを用いてOpenGLのコンテクストを設定し、ウィンドを作ります。すべてのインクルードおよびライブラリディレクトリが正しく、リンカの設定にあるライブラリ名が実際のライブラリ名と対応していることを確認してください。 +</p> + +<h2>Additional resources</h2> +<h2>参考</h2> +<ul> + <li><a href="http://www.glfw.org/docs/latest/window_guide.html" target="_blank">GLFW: Window Guide</a>: official GLFW guide on setting up and configuring a GLFW window.</li> + <li><a href="http://www.glfw.org/docs/latest/window_guide.html" target="_blank">GLFW: Window Guide</a>: GLFWのセットアップおよびウィンドウの設定に関する公式ガイド。</li> + <li><a href="http://www.opengl-tutorial.org/miscellaneous/building-your-own-c-application/" target="_blank">Building applications</a>: provides great info about the compilation/linking process of your application and a large list of possible errors (plus solutions) that may come up.</li> + <li><a href="http://www.opengl-tutorial.org/miscellaneous/building-your-own-c-application/" target="_blank">Building applications</a>: コンパイルとリンクに関する情報及び予想されるエラーとその解決法。</li> + <li><a href="http://wiki.codeblocks.org/index.php?title=Using_GLFW_with_Code::Blocks" target="_blank">GLFW with Code::Blocks</a>: building GLFW in Code::Blocks IDE.</li> + <li><a href="http://wiki.codeblocks.org/index.php?title=Using_GLFW_with_Code::Blocks" target="_blank">GLFW with Code::Blocks</a>: Code::BlocksにおけるGLFWのビルド方法。</li> + <li><a href="http://www.cmake.org/runningcmake/" target="_blank">Running CMake</a>: short overview of how to run CMake on both Windows and Linux.</li> + <li><a href="http://www.cmake.org/runningcmake/" target="_blank">Running CMake</a>: WindowsとLinux両方におけるCMakeの概要。</li> + <li><a href="https://learnopengl.com/demo/autotools_tutorial.txt" target="_blank">Writing a build system under Linux</a>: an autotools tutorial by Wouter Verholst on how to write a build system in Linux.</li> + <li><a href="https://learnopengl.com/demo/autotools_tutorial.txt" target="_blank">Writing a build system under Linux</a>: Wouter Verholstによるautotoolsのチュートリアル: Linuxにおけるビルドシステムの書き方。</li> + <li><a href="https://github.com/Polytonic/Glitter" target="_blank">Polytonic/Glitter</a>: a simple boilerplate project that comes pre-configured with all relevant libraries; great for if you want a sample project without the hassle of having to compile all the libraries yourself.</li> + <li><a href="https://github.com/Polytonic/Glitter" target="_blank">Polytonic/Glitter</a>: 必要なライブラリがあらかじめ設定された、雛形のようなプロジェクト。ライブラリを自分でコンパイルしないですむサンプルプロジェクトがほしい場合に最適。</li> +</ul> + + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Hello-Triangle.html b/pub/Getting-started/Hello-Triangle.html @@ -0,0 +1,1156 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Hello Triangle</h1> + <h1 id="content-title">はじめての三角形</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Hello-Triangle</h1> +<p> + In OpenGL everything is in 3D space, but the screen or window is a 2D array of pixels so a large part of OpenGL's work is about transforming all 3D coordinates to 2D pixels that fit on your screen. The process of transforming 3D coordinates to 2D pixels is managed by the <def>graphics pipeline</def> of OpenGL. The graphics pipeline can be divided into two large parts: the first transforms your 3D coordinates into 2D coordinates and the second part transforms the 2D coordinates into actual colored pixels. In this chapter we'll briefly discuss the graphics pipeline and how we can use it to our advantage to create fancy pixels. +OpenGLにおいてあらゆることは三次元空間上でおこります。しかしスクリーンやウィンドウはピクセルの二次元配列です。OpenGLの仕事のほとんどは三次元空間でのできごとを二次元のピクセルにおとしこむことです。三次元から二次元への変換は<def>グラフィックパイプライン</def>により処理されます。グラフィックパイプラインは大きくわけて二つのことに分割できます: ひとつめは三次元の座標を二次元に変換すること、ふたつめはその二次元の座標を実際の色に変換することです。この章ではグラフィックパイプラインの概要およびそれを使っていい感じに描画する方法をざっくりと説明します。 +</p> + +<!--<note> + There is a difference between a 2D coordinate and a pixel. A 2D coordinate is a very precise representation of where a point is in 2D space, while a 2D pixel is an approximation of that point limited by the resolution of your screen/window. +</note>--> + +<p> + The graphics pipeline takes as input a set of 3D coordinates and transforms these to colored 2D pixels on your screen. The graphics pipeline can be divided into several steps where each step requires the output of the previous step as its input. All of these steps are highly specialized (they have one specific function) and can easily be executed in parallel. Because of their parallel nature, graphics cards of today have thousands of small processing cores to quickly process your data within the graphics pipeline. The processing cores run small programs on the GPU for each step of the pipeline. These small programs are called <def>shaders</def>. +グラフィックパイプラインは三次元の座標を入力として受けとり、色の付いた二次元のピクセルをスクリーンに表示します。グラフィックパイプラインはいくつかのステップに分割できます。それぞれのステップでは入力として前のステップの出力を受けとります。各ステップはそれぞれの仕事に特化していて(それぞれの仕事に特化した関数をもっています)、簡単に並列化することができます。そのため今日のグラフィックカードは何千もの小さな演算装置を持ち、グラフィックパイプライン中で高速に処理できるようになっています。パイプラインの各ステップで、演算装置が小さなプログラムを実行します。この小さなプログラムを<def>シェーダー</def>といいます。 +</p> + +<p> + Some of these shaders are configurable by the developer which allows us to write our own shaders to replace the existing default shaders. This gives us much more fine-grained control over specific parts of the pipeline and because they run on the GPU, they can also save us valuable CPU time. Shaders are written in the <def>OpenGL Shading Language</def> (<def>GLSL</def>) and we'll delve more into that in the next chapter. +シェーダーのうちいくつかのものはデフォルトのものを開発者自身によって書いたものに変更できます。これによりパイプラインの特定の場所をより細かくコントロールできます。されにシェーダーがGPU上で実行されているので、貴重なCPUの時間を節約できます。シェーダーは<def>OpenGLシェーディンク言語</def>(<def>GLSL</def>)で記載されます。GLSLについては次の章で詳しく解説します。 +</p> + +<p> + Below you'll find an abstract representation of all the stages of the graphics pipeline. Note that the blue sections represent sections where we can inject our own shaders. +以下にグラフィックパイプラインのおおまかな流れを示します。青い部分は自作のシェーダーを指しこめる場所を表しています。 +</p> + +<img src="/img/getting-started/pipeline.png" class="clean" alt="The OpenGL graphics pipeline with shader stages" /> + + +<p> + As you can see, the graphics pipeline contains a large number of sections that each handle one specific part of converting your vertex data to a fully rendered pixel. We will briefly explain each part of the pipeline in a simplified way to give you a good overview of how the pipeline operates. +ごらんのように、グラフィックパイプラインにはたくさんの処理が含まれます。頂点のデータを描画用のピクセルに変換する過程でそれぞれの役割を分担しているのです。パイプラインの動きの概観をつかんでいただくために、それぞれの処理内容を簡単に見ていきましょう。 +</p> + +<p> + As input to the graphics pipeline we pass in a list of three 3D coordinates that should form a triangle in an array here called <code>Vertex Data</code>; this vertex data is a collection of vertices. A <def>vertex</def> is a collection of data per 3D coordinate. This vertex's data is represented using <def>vertex attributes</def> that can contain any data we'd like, but for simplicity's sake let's assume that each vertex consists of just a 3D position and some color value. +グラフィックパイプラインの入力として、三次元空間に三角形を構成する三つの座標を用意します。この入力のような、頂点の組を<code>頂点データ</code>と呼びます。<def>頂点</def>とは、三次元のそれぞれの座標軸にそった値の組です。頂点データは<def>頂点属性</def>といい、任意の情報を持たせることができます。ここでは簡単のために各頂点が座標と色の情報だけを保持しているものとします。 +</p> + +<note> + In order for OpenGL to know what to make of your collection of coordinates and color values OpenGL requires you to hint what kind of render types you want to form with the data. Do we want the data rendered as a collection of points, a collection of triangles or perhaps just one long line? Those hints are called <def>primitives</def> and are given to OpenGL while calling any of the drawing commands. Some of these hints are <var>GL_POINTS</var>, <var>GL_TRIANGLES</var> and <var>GL_LINE_STRIP</var>. +座標や色の組からなにをしたいかをOpenGLに伝えるために、描画のタイプを指定する必要があります。点を描画するのか、三角形か、あるいは一本の長い線か。このような描画タイプのことを<def>プリミティブ</def>といい、OpenGLがなにかを描くときに利用されます。プリミティブには<var>GL_POINTS</var>、<var>GL_TRIANGLES</var>、<var>GL_LINE_STRIP</var>等があります。 +</note> + +<p> + The first part of the pipeline is the <def>vertex shader</def> that takes as input a single vertex. The main purpose of the vertex shader is to transform 3D coordinates into different 3D coordinates (more on that later) and the vertex shader allows us to do some basic processing on the vertex attributes. +パイプラインの最初の処理は入力としてひとつの頂点をとる<def>頂点シェーダー</def>です。頂点シェーダーの主な目的は三次元の座標を別の三次元の座標に変換することです(あとで詳しく説明します)。頂点シェーターでは頂点属性に対する基本的な処理も可能です。 +</p> + +<p> + The <def>primitive assembly</def> stage takes as input all the vertices (or vertex if <var>GL_POINTS</var> is chosen) from the vertex shader that form a primitive and assembles all the point(s) in the primitive shape given; in this case a triangle. +<def>プリミティブアセンブリ</def>では入力として頂点シェーダからプリミティブを構成する頂点の組(<var>GL_POINTS</var>の場合はひとつだけ)をとり、指定されたプリミティブ(今回の場合は三角形)を形成します。 +</p> + +<p> + The output of the primitive assembly stage is passed to the <def>geometry shader</def>. The geometry shader takes as input a collection of vertices that form a primitive and has the ability to generate other shapes by emitting new vertices to form new (or other) primitive(s). In this example case, it generates a second triangle out of the given shape. +プリミティブアセンブリからの出力は<def>ジオメトリシェーダー</def>に渡されます。ジオメトリシェーダーは入力としてプリミティブを構成する頂点の組をとり、頂点を追加することで新しい(あるいは他の)プリミティブを作成することができます。こんかいの例では与えられた図形からふたつ目の三角形を生み出しています。 + +</p> + +<p> + The output of the geometry shader is then passed on to the <def>rasterization stage</def> where it maps the resulting primitive(s) to the corresponding pixels on the final screen, resulting in fragments for the fragment shader to use. Before the fragment shaders run, <def>clipping</def> is performed. Clipping discards all fragments that are outside your view, increasing performance. +ジオメトリシェーダーの出力は<def>ラスタライザーステージ</def>に渡されます。ここでは出力されたプリミティブを最終的にスクリーンに表示されるピクセルと対応したデータに落しこまれ、フラグメントシェーダーが利用できるフラグメントになります。フラグメントシェーダーが実行される前に、<def>クリッピング</def>が行われます。これは画面の外に隠れて見えなくなる部分のフラグメントを捨て、処理速度を向上させるものです。 +</p> + +<note> + A fragment in OpenGL is all the data required for OpenGL to render a single pixel. +フラグメントとは、OpenGLがひとつのピクセルを表示するために必要なすべてのデータのことです。 +</note> + +<p> + The main purpose of the <def>fragment shader</def> is to calculate the final color of a pixel and this is usually the stage where all the advanced OpenGL effects occur. Usually the fragment shader contains data about the 3D scene that it can use to calculate the final pixel color (like lights, shadows, color of the light and so on). +<def>フラグメントシェーター</def>の主な目的は最終的なピクセルの色を計算することです。通常このシェーダーにおいてすべての先進的な効果が適用されます。フラグメントシェーダーは普通、最終的なピクセルの色を決定するために利用する三次元空間上の情報を持っています(照明、影、光の色などです)。 +</p> + + +<p> + After all the corresponding color values have been determined, the final object will then pass through one more stage that we call the <def>alpha test</def> and <def>blending</def> stage. This stage checks the corresponding depth (and stencil) value (we'll get to those later) of the fragment and uses those to check if the resulting fragment is in front or behind other objects and should be discarded accordingly. The stage also checks for <def>alpha</def> values (alpha values define the opacity of an object) and <def>blends</def> the objects accordingly. So even if a pixel output color is calculated in the fragment shader, the final pixel color could still be something entirely different when rendering multiple triangles. +すべての色が決定されたあと、出力されたオブジェクトは<def>アルファテスト</def>および<def>ブレンディング</def>ステージとよばれるもうひとつのステージで処理されます。フラグメントの深度およびステンシル値(あとで詳しく見ます)がチェックされ、フラグメントどうしの前後関係を確認し、表示すべきかどうかを決定します。このステージで<def>アルファ</def>値(物体の透明度)も確認され、その値に応じてオブジェクトが<def>ブレンド</def>されます。そのためフラグメントシェーダーによってピクセルの色が計算されても、複数の三角形を描画する場合、その出力値が実際に表示されるものとまったく違ったものになる可能性があります。 +</p> + +<p> + As you can see, the graphics pipeline is quite a complex whole and contains many configurable parts. However, for almost all the cases we only have to work with the vertex and fragment shader. The geometry shader is optional and usually left to its default shader. There is also the tessellation stage and transform feedback loop that we haven't depicted here, but that's something for later. +ここまで見てきたようにグラフィックスパイプラインは設定可能な部分を多く含み、全体として非常に複雑です。しかし基本的には頂点シェーダーとフラグメントシェーダー以外をさわることはありません。ジオメトリシェーダーは通常デフォルトのシェーダーのままにしておきます。この他にもテッセレーションステージや変換フィードバックループといったものもありますが、それらについては後で説明します。 +</p> + +<p> + In modern OpenGL we are <strong>required</strong> to define at least a vertex and fragment shader of our own (there are no default vertex/fragment shaders on the GPU). For this reason it is often quite difficult to start learning modern OpenGL since a great deal of knowledge is required before being able to render your first triangle. Once you do get to finally render your triangle at the end of this chapter you will end up knowing a lot more about graphics programming. +現在のOpenGLではすくなくとも頂点シェーダーとフラグメントシェーダーは自分で用意することが<strong>必要</strong>です(デフォルトのシェーダーは存在しません)。そのため初学者が初めて三角形を表示させるために要求される知識量が多くなり、これがOpenGLの学習を難しくしている要因です。この章を読み、最初の三角形を描画することができたときには、グラフィックプログラミングに関して多くの知識を得ていることでしょう。 +</p> + +<h2>Vertex input</h2> +<h2>頂点の入力</h2> +<p> + To start drawing something we have to first give OpenGL some input vertex data. OpenGL is a 3D graphics library so all coordinates that we specify in OpenGL are in 3D (<code>x</code>, <code>y</code> and <code>z</code> coordinate). OpenGL doesn't simply transform <strong>all</strong> your 3D coordinates to 2D pixels on your screen; OpenGL only processes 3D coordinates when they're in a specific range between <code>-1.0</code> and <code>1.0</code> on all 3 axes (<code>x</code>, <code>y</code> and <code>z</code>). All coordinates within this so called <def>normalized device coordinates</def> range will end up visible on your screen (and all coordinates outside this region won't). +描画するためにはまずOpenGLに頂点のデータを入力として与えなければなりません。OpenGLは三次元のグラフィックライブラリなので、すべての座標は三次元です(<code>x</code>座標、<code>y</code>座標、そして<code>z</code>座標です)。OpenGLは<strong>すべて</strong>の三次元座標を単に二次元のピクセルに変換するわけではありません。<code>x</code>、<code>y</code>、<code>z</code>のすべての座標が<code>-1.0</code>と<code>1.0</code>のあいだにあるものだけを処理します。この範囲にある座標は<def>正規化デバイス座標系</def>と呼ばれます。この範囲にあるものだけが最終的にスクリーンに表示され、この外側のものは表示されません。 +</p> + +<p> + Because we want to render a single triangle we want to specify a total of three vertices with each vertex having a 3D position. We define them in normalized device coordinates (the visible region of OpenGL) in a <code>float</code> array: +ここでは三角形を表示したいので、三次元空間上のみっつの頂点を指定します。<code>float</code>の配列として、正規化デバイス座標系(OpenGLにおいて可視の領域)中に定義しましょう: +</p> + +<pre><code> +float vertices[] = { + -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + 0.0f, 0.5f, 0.0f +}; +</code></pre> + +<p> + Because OpenGL works in 3D space we render a 2D triangle with each vertex having a <code>z</code> coordinate of <code>0.0</code>. This way the <em>depth</em> of the triangle remains the same making it look like it's 2D. +OpenGLは三次元空間上で動作するので、二次元の三角形を描画する場合は各頂点の<code>z</code>座標を<code>0.0</code>にします。こうすることで各頂点の<em>深度</em>が同じになり、二次元であるように見えます。 +</p> + +<note> + <strong>Normalized Device Coordinates (NDC)</strong><br/> + <strong>正規化デバイス座標系(NDC)</strong><br/> + <p> + Once your vertex coordinates have been processed in the vertex shader, they should be in <def>normalized device coordinates</def> which is a small space where the <code>x</code>, <code>y</code> and <code>z</code> values vary from <code>-1.0</code> to <code>1.0</code>. Any coordinates that fall outside this range will be discarded/clipped and won't be visible on your screen. Below you can see the triangle we specified within normalized device coordinates (ignoring the <code>z</code> axis): + 頂点シェーダーで処理された頂点の座標は<def>正規化デバイス座標系</def>の中におさまらなければいけません。正規化デバイス座標系とは、<code>x</code>、<code>y</code>、<code>z</code>座標がすべて<code>-1.0</code>と<code>1.0</code>のあいだにあるような小さな立方体の空間です。この座標の外にあるものはすべて切り捨てられ、スクリーン上では見えなくなります。下の画像は先程正規化デバイス座標系におさまるように指定した三角形です(<code>z</code>軸は無視しています)。 + </p> + <img src="/img/getting-started/ndc.png" class="clean" alt="2D Normalized Device Coordinates as shown in a graph"/> + <p> + Unlike usual screen coordinates the positive y-axis points in the up-direction and the <code>(0,0)</code> coordinates are at the center of the graph, instead of top-left. Eventually you want all the (transformed) coordinates to end up in this coordinate space, otherwise they won't be visible. + 通常のスクリーンの座標系とは違い、y軸は上を指し、原点は左上ではなく中央に位置します。最終的に変換後のすべての座標がこの中におさまるようにしましょう。さもなければ表示されません。 +</p> +<p> + Your NDC coordinates will then be transformed to <def>screen-space coordinates</def> via the <def>viewport transform</def> using the data you provided with <fun><function id='22'>glViewport</function></fun>. The resulting screen-space coordinates are then transformed to fragments as inputs to your fragment shader. +NDCにおとしこんだ座標は<def>ビューポート変換</def>により<def>スクリーン座標系</def>に変換されます。ビューポート変換は<fun><function id='22'>glViewport</function></fun>で設定した情報を利用します。スクリーン座標系の座標はフラグメントシェーダーの入力となるフラグメントに変換されます。 + </p> +</note> + +<p> + With the vertex data defined we'd like to send it as input to the first process of the graphics pipeline: the vertex shader. This is done by creating memory on the GPU where we store the vertex data, configure how OpenGL should interpret the memory and specify how to send the data to the graphics card. The vertex shader then processes as much vertices as we tell it to from its memory. +先程定義した頂点のデータを、グラフィックスパイプラインの最初の処理である頂点シェーダーに入力として送りましょう。そのためにまず頂点データを格納する上のメモリを確保し、OpenGLにそのメモリ中にどんなデータが入っているのか、およびどのようにデータをグラフィックカードに送信するのかを設定します。そうすることで頂点シェーダーがGPUのメモリを読み、指定した分だけ頂点のデータを処理してくれます。 +</p> + +<p> + We manage this memory via so called <def>vertex buffer objects</def> (<def>VBO</def>) that can store a large number of vertices in the GPU's memory. The advantage of using those buffer objects is that we can send large batches of data all at once to the graphics card, and keep it there if there's enough memory left, without having to send data one vertex at a time. Sending data to the graphics card from the CPU is relatively slow, so wherever we can we try to send as much data as possible at once. Once the data is in the graphics card's memory the vertex shader has almost instant access to the vertices making it extremely fast +このメモリはGPUのメモリに大量の頂点を保持できる<def>頂点バッファオブジェクト</def>(<def>VBO</def>)を通して管理されます。このバッファの利点は大きなかたまりになったデータをいっぺんにグラフィックカードに送信し、メモリに十分な空きがなくてもそのデータを保持し続けてくれることです。頂点データをひとつずつ送信しなくてすむのです。CPUからグラフィックカードへのデータの送信は比較的遅いので、可能な限り多くのデータを一度に送りたいのです。いったん頂点データがグラフィックカードのメモリに置かれると、頂点シェーダーが即座にデータを参照できるので高速な処理が可能になります。 +</p> + +<p> + A vertex buffer object is our first occurrence of an OpenGL object as we've discussed in the <a href="https://learnopengl.com/Getting-Started/OpenGL" target="_blank">OpenGL</a> chapter. Just like any object in OpenGL, this buffer has a unique ID corresponding to that buffer, so we can generate one with a buffer ID using the <fun><function id='12'>glGenBuffers</function></fun> function: +<a href="https://learnopengl.com/Getting-Started/OpenGL" target="_blank">OpenGL</a>の章で議論したように、頂点バッファオブジェクトはわれわれが最初に遭遇したOpenGLオブジェクトです。このバッファもOpenGLの他のオブジェクトと同様、一意のIDを割り当てられるので、<fun><function id='12'>glGenBuffers</function></fun>を使ってオブジェクトを作成すると同時にバッファIDを取得できます: +</p> + +<pre class="cpp"><code> +unsigned int VBO; +<function id='12'>glGenBuffers</function>(1, &amp;VBO); +</code></pre> + +<p> + OpenGL has many types of buffer objects and the buffer type of a vertex buffer object is <var>GL_ARRAY_BUFFER</var>. OpenGL allows us to bind to several buffers at once as long as they have a different buffer type. We can bind the newly created buffer to the <var>GL_ARRAY_BUFFER</var> target with the <fun><function id='32'>glBindBuffer</function></fun> function: +OpenGLにはさまざまな型のバッファオブジェクトがあります。頂点バッファオブジェクトの型は<var>GL_ARRAY_BUFFER</var>です。型が異なる限り、同時に複数のバッファを紐付けすることができます。新しく作ったバッファは<fun><function id='32'>glBindBuffer</function></fun>により<var>GL_ARRAY_BUFFER</var>に紐付けできます: +</p> + +<pre><code> +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +</code></pre> + +<p> + From that point on any buffer calls we make (on the <var>GL_ARRAY_BUFFER</var> target) will be used to configure the currently bound buffer, which is <var>VBO</var>. Then we can make a call to the + <fun><function id='31'>glBufferData</function></fun> function that copies the previously defined vertex data into the buffer's memory: +上記のようにバッファを紐付けすることで、以降われわれが行うバッファの操作(<var>GL_ARRAY_BUFFER</var>をターゲットとするもの)は現在紐付いているもの(ここでは<var>VBO</var>)にたいして行われます。バッファの紐付けができたら、<fun><function id='31'>glBufferData</function></fun>を呼び、先程定義した頂点データをバッファのメモリにコピーしましょう: +</p> + +<pre><code> +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); +</code></pre> + +<p> + <fun><function id='31'>glBufferData</function></fun> is a function specifically targeted to copy user-defined data into the currently bound buffer. Its first argument is the type of the buffer we want to copy data into: the vertex buffer object currently bound to the <var>GL_ARRAY_BUFFER</var> target. The second argument specifies the size of the data (in bytes) we want to pass to the buffer; a simple <code>sizeof</code> of the vertex data suffices. The third parameter is the actual data we want to send. +<fun><function id='31'>glBufferData</function></fun>は現在紐付いているバッファに、ユーザーが独自に定義したデータをコピーするための関数です。最初の引数はデータをコピーしたいバッファの型です。この例において現在頂点バッファオブジェクトは<var>GL_ARRAY_BUFFER</var>に紐付いています。二番目の引数はバッファに渡したいデータのサイズ(バイト単位)です。頂点データに<code>sizeof</code>を適応したもので十分です。三番目はコピーするデータです。 +</p> + +<p> + The fourth parameter specifies how we want the graphics card to manage the given data. This can take 3 forms: +四番目の引数ではコピーしたデータをグラフィックカードがどのように管理するかを指定します。管理方法は以下の三つから選びます: +</p> + + <ul> + <li><var>GL_STREAM_DRAW</var>: the data is set only once and used by the GPU at most a few times.</li> + <li><var>GL_STATIC_DRAW</var>: the data is set only once and used many times.</li> + <li><var>GL_DYNAMIC_DRAW</var>: the data is changed a lot and used many times.</li> + </ul> + <ul> + <li><var>GL_STREAM_DRAW</var>: データは一度だけセットされ、GPUは最大でも数回程度しか利用しない。</li> + <li><var>GL_STATIC_DRAW</var>: データは一度だけセットされ、繰り返し利用される。</li> + <li><var>GL_DYNAMIC_DRAW</var>: データは頻繁に変更され、繰り返し利用される。</li> + </ul> + +<p> + The position data of the triangle does not change, is used a lot, and stays the same for every render call so its usage type should best be <var>GL_STATIC_DRAW</var>. If, for instance, one would have a buffer with data that is likely to change frequently, a usage type of <var>GL_DYNAMIC_DRAW</var> ensures the graphics card will place the data in memory that allows for faster writes. +三角形の位置のデータは変更されることはなく、何度も利用されるので、<var>GL_STATIC_DRAW</var>を指定します。これが例えば頻繁にデータの書き換えがおこるバッファであれば、<var>GL_DYNAMIC_DRAW</var>を選ぶことでグラフィックカードはメモリにおいてより高速に書き換えられる場所にデータを配置します。 +</p> + +<p> + As of now we stored the vertex data within memory on the graphics card as managed by a vertex buffer object named <var>VBO</var>. Next we want to create a vertex and fragment shader that actually processes this data, so let's start building those. +これで<var>VBO</var>と呼ばれる頂点バッファオブジェクトによって管理されているグラフィックカードのメモリ上に頂点データを配置できました。続いて頂点シェーダーとフラグメントシェーダーを作成し、このデータを実際に処理していきましょう。 +</p> + +<h2>Vertex shader</h2> +<h2>頂点シェーダー</h2> +<p> + The vertex shader is one of the shaders that are programmable by people like us. Modern OpenGL requires that we at least set up a vertex and fragment shader if we want to do some rendering so we will briefly introduce shaders and configure two very simple shaders for drawing our first triangle. In the next chapter we'll discuss shaders in more detail. +頂点シェーダーはわれわれがプログラミングできるシェーダーです。現行のOpenGLにおいて、描画のためには少なくとも頂点シェーダーとフラグメントシェーダーを自分達で用意する必要があります。ここでは簡単にシェーダーについて紹介し、最初の三角形を描画するための簡単なシェーダーを作成します。次の節ではシェーダーについてもっと詳しく見ていきます。 +</p> + +<p> + The first thing we need to do is write the vertex shader in the shader language GLSL (OpenGL Shading Language) and then compile this shader so we can use it in our application. Below you'll find the source code of a very basic vertex shader in GLSL: +最初に必要なのはGLSL(OpenGL Shading Language)というシェーダー言語により、頂点シェーダーを書き、アプリケーションで利用できるようにコンパイルすることです。以下にGLSLで記述された最も基本的な頂点シェーダーを示します: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +void main() +{ + gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); +} +</code></pre> + +<p> + As you can see, GLSL looks similar to C. Each shader begins with a declaration of its version. Since OpenGL 3.3 and higher the version numbers of GLSL match the version of OpenGL (GLSL version 420 corresponds to OpenGL version 4.2 for example). We also explicitly mention we're using core profile functionality. +ご覧のようにGLSLはC言語に近いです。シェーダーはバージョンの宣言から記述します。OpenGL 3.3以降ではGLSLのバージョンはOpenGLのバージョンと呼応します(例えばGLSLバージョン420はOpenGLのバージョン4.2に対応します)。加えてコアプロファイルを仕様することを明記します。 +</p> + +<p> + Next we declare all the input vertex attributes in the vertex shader with the <code>in</code> keyword. Right now we only care about position data so we only need a single vertex attribute. GLSL has a vector datatype that contains 1 to 4 floats based on its postfix digit. Since each vertex has a 3D coordinate we create a <code>vec3</code> input variable with the name <var>aPos</var>. We also specifically set the location of the input variable via <code>layout (location = 0)</code> and you'll later see that why we're going to need that location. +続いて<code>in</code>というキーワードで、入力となる頂点属性をすべて宣言します。とりあえず位置のデータだけを処理するので、必要な頂点属性はひとつだけです。GLSLは<code>float</code>の1から4つ持つベクトルのデータ型をもちます。ベクトルの次元はデータ型の後ろに付いている数字に対応しています。頂点の座標は三次元なので<code>vec3</code>型の入力を<var>aPos</var>という名前で作成します。<code>layout (location = 0)</code>という記述により、入力となる変数の位置も指定します。なぜこのような場所の指定が必要になるのかは後ほど説明します。 +</p> + +<note> + <strong>Vector</strong><br/> + <strong>ベクトル</strong><br/> + In graphics programming we use the mathematical concept of a vector quite often, since it neatly represents positions/directions in any space and has useful mathematical properties. A vector in GLSL has a maximum size of 4 and each of its values can be retrieved via <code>vec.x</code>, <code>vec.y</code>, <code>vec.z</code> and <code>vec.w</code> respectively where each of them represents a coordinate in space. Note that the <code>vec.w</code> component is not used as a position in space (we're dealing with 3D, not 4D) but is used for something called <def>perspective division</def>. We'll discuss vectors in much greater depth in a later chapter. +グラフィックプログラミングにおいて、数学の概念であるベクトルがよく利用されます。空間上での位置や方向をうまく記述でき、その数学的な性質が有用だからです。GLSLにおけるベクトルのサイズは最大で4です。要素は<code>vec.x</code>、<code>vec.y</code>、<code>vec.z</code>、<code>vec.w</code>により利用でき、空間上でのそれぞれの座標をあらわします。ただしわれわれが扱っているのは三次元空間であり四次元ではないので、<code>vec.w</code>は空間上の位置をあらわしていないことに注意してください。これは<def>パースペクティブディビジョン</def>を行うために利用されるものです。あとの節でベクトルについてさらに深掘りします。 +</note> + +<p> + To set the output of the vertex shader we have to assign the position data to the predefined <var>gl_Position</var> variable which is a <code>vec4</code> behind the scenes. At the end of the <fun>main</fun> function, whatever we set <var>gl_Position</var> to will be used as the output of the vertex shader. Since our input is a vector of size 3 we have to cast this to a vector of size 4. We can do this by inserting the <code>vec3</code> values inside the constructor of <code>vec4</code> and set its <code>w</code> component to <code>1.0f</code> (we will explain why in a later chapter). +頂点シェーダーの出力を設定するためには、あらかじめ定義された四次元ベクトルである<var>gl_Position</var>という変数に位置のデータを割り当てる必要があります。<fun>main</fun>関数の終了時点で<var>gl_Position</var>に割り当てられているものが、頂点シェーダーの出力となります。入力のベクトルは三次元なので、これを四次元に変換する必要があります。ここでは<code>vec3</code>の値を<code>vec4</code>のコンストラクタ内にさしこみ、<code>w</code>の値は<code>1.0f</code>としておきましょう(あとで詳しく説明します)。 +</p> + +<p> + The current vertex shader is probably the most simple vertex shader we can imagine because we did no processing whatsoever on the input data and simply forwarded it to the shader's output. In real applications the input data is usually not already in normalized device coordinates so we first have to transform the input data to coordinates that fall within OpenGL's visible region. +こうして構成した頂点シェーダーはなんの処理も行わずに入力値をそのまま出力しているだけなので、おそらく最も単純なものです。実際のアプリケーションでは通常入力データは正規化されていないのでまずはOpenGLの可視領域におさまるように変換しないといけません。 +</p> + +<h2>Compiling a shader</h2> +<h2>シェーダーのコンパイル</h2> +<p> + We take the source code for the vertex shader and store it in a const C string at the top of the code file for now: +それではコードの一番上にC言語のの文字列定数として頂点シェーダーのソースコードを記述しましょう: +</p> + +<pre><code> +const char *vertexShaderSource = "#version 330 core\n" + "layout (location = 0) in vec3 aPos;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" + "}\0"; +</code></pre> + +<p> + In order for OpenGL to use the shader it has to dynamically compile it at run-time from its source code. The first thing we need to do is create a shader object, again referenced by an ID. So we store the vertex shader as an <code>unsigned int</code> and create the shader with <fun><function id='37'>glCreateShader</function></fun>: +OpenGLがシェーダーを利用するには実行時にソースコード内から動的にコンパイルする必要があります。まずはじめにこれまたIDにより参照されるシェーダーオブジェクトを作成しないといけません。頂点シェーダーを<code>unsigned int</code>として保存し、シェーダーを<fun><function id='37'>glCreateShader</function></fun>により作成します: +</p> + +<pre><code> +unsigned int vertexShader; +vertexShader = <function id='37'>glCreateShader</function>(GL_VERTEX_SHADER); +</code></pre> + +<p> + We provide the type of shader we want to create as an argument to <fun><function id='37'>glCreateShader</function></fun>. Since we're creating a vertex shader we pass in <var>GL_VERTEX_SHADER</var>. +作成するシェーダーの種類は<fun><function id='37'>glCreateShader</function></fun>の引数で指定します。今回作成するのは頂点シェーダーなので、<var>GL_VERTEX_SHADER</var>を渡します。 +</p> + +<p> + Next we attach the shader source code to the shader object and compile the shader: +次にシェーダーのソースコードをシェーダーオブジェクトに割り当てコンパイルします: +</p> + +<pre class="cpp"><code> +<function id='42'>glShaderSource</function>(vertexShader, 1, &amp;vertexShaderSource, NULL); +<function id='38'>glCompileShader</function>(vertexShader); +</code></pre> + +<p> + The <fun><function id='42'>glShaderSource</function></fun> function takes the shader object to compile to as its first argument. The second argument specifies how many strings we're passing as source code, which is only one. The third parameter is the actual source code of the vertex shader and we can leave the 4th parameter to <code>NULL</code>. +<fun><function id='42'>glShaderSource</function></fun>はコンパイルするシェーダーオブジェクトを最初の引数にとります。二つ目の引数はソースコードとしていくつの文字列を渡すかを指定します。ここでは一つだけなので1を指定します。三番目は頂点シェーダーのソースコードそのもので、四番目は<code>NULL</code>のままで大丈夫です。 +</p> + +<note> + <p> + You probably want to check if compilation was successful after the call to <fun><function id='38'>glCompileShader</function></fun> and if not, what errors were found so you can fix those. Checking for compile-time errors is accomplished as follows: +<fun><function id='38'>glCompileShader</function></fun>を呼び出した後、コンパイルが通ったかどうか、どのようなエラーが発生したか確認したいものです。コンパイル時のエラーの確認は以下のようにできます: + </p> + +<pre class="cpp"><code> +int success; +char infoLog[512]; +<function id='39'>glGetShaderiv</function>(vertexShader, GL_COMPILE_STATUS, &amp;success); +</code></pre> + +<p> + First we define an integer to indicate success and a storage container for the error messages (if any). Then we check if compilation was successful with <fun><function id='39'>glGetShaderiv</function></fun>. If compilation failed, we should retrieve the error message with <fun><function id='40'>glGetShaderInfoLog</function></fun> and print the error message. +はじめに成功したかどうかを格納するための整数と、エラーメッセージがでた場合にそれを格納するための変数を宣言します。その後<fun><function id='39'>glGetShaderiv</function></fun>によりコンパイルが成功したかどうか確認します。失敗していた場合、<fun><function id='40'>glGetShaderInfoLog</function></fun>を用いてエラーメッセージを取り出し、表示するようにします。 + </p> + +<pre><code> +if(!success) +{ + <function id='40'>glGetShaderInfoLog</function>(vertexShader, 512, NULL, infoLog); + std::cout &lt;&lt; "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" &lt;&lt; infoLog &lt;&lt; std::endl; +} +</code></pre> +</note> + +<p> + If no errors were detected while compiling the vertex shader it is now compiled. +エラーが出なければシェーダーのコンパイルは成功です。 +</p> + +<h2>Fragment shader</h2> +<h2>フラグメントシェーダー</h2> +<p> + The fragment shader is the second and final shader we're going to create for rendering a triangle. The fragment shader is all about calculating the color output of your pixels. To keep things simple the fragment shader will always output an orange-ish color. +フラグメントシェーダーは三角形を描画するために作成する二つ目にして最後のシェーダーです。フラグメントシェーダーはピクセルの色を計算するためのシェーダーです。ここでは簡単のためにフラグメントシェーダーは常にオレンジ色を出力するようにします。 +</p> + +<note> + Colors in computer graphics are represented as an array of 4 values: the red, green, blue and alpha (opacity) component, commonly abbreviated to RGBA. When defining a color in OpenGL or GLSL we set the strength of each component to a value between <code>0.0</code> and <code>1.0</code>. If, for example, we would set red to <code>1.0</code> and green to <code>1.0</code> we would get a mixture of both colors and get the color yellow. Given those 3 color components we can generate over 16 million different colors! +コンピュータグラフィックスにおいて、色は4つの数値で表現されます。赤、緑、青、アルファ(透明度)の4つで、一般的にRGBAと略記されます。OpenGLやGLSLにおいて色を定義する場合、それぞれの要素を<code>0.0</code>と<code>1.0</code>のあいだの数値で表現します。例えば赤を<code>1.0</code>、緑を<code>1.0</code>にした場合、赤と緑をまぜた色である黄色が得られます。3色の組み合わせにより、160万色以上を表現できます。 +</note> + +<pre><code> +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); +} +</code></pre> + +<p> + The fragment shader only requires one output variable and that is a vector of size 4 that defines the final color output that we should calculate ourselves. We can declare output values with the <code>out</code> keyword, that we here promptly named <var>FragColor</var>. Next we simply assign a <code>vec4</code> to the color output as an orange color with an alpha value of <code>1.0</code> (<code>1.0</code> being completely opaque). +フラグメントシェーダーに必要な出力は最終的な色を決定する四次元ベクトルだけです。このベクトルはわれわれ自身で計算しなければいけません。出力値は<code>out</code>で指定でき、ここでは<var>FragColor</var>という名前にしています。続いて単に<code>vec4</code>にアルファ値が<code>1.0</code>のオレンジ色を割り当てます(アルファ値<code>1.0</code>は完全に不透明であることを意味します)。 +</p> + +<p> + The process for compiling a fragment shader is similar to the vertex shader, although this time we use the <var>GL_FRAGMENT_SHADER</var> constant as the shader type: +フラグメントシェーダーのコンパイルは頂点シェーダーとほぼ同じです。ただし今回はシェーダーの種類として<var>GL_FRAGMENT_SHADER</var>を指定します: +</p> + +<pre class="cpp"><code> +unsigned int fragmentShader; +fragmentShader = <function id='37'>glCreateShader</function>(GL_FRAGMENT_SHADER); +<function id='42'>glShaderSource</function>(fragmentShader, 1, &amp;fragmentShaderSource, NULL); +<function id='38'>glCompileShader</function>(fragmentShader); +</code></pre> + +<p> + Both the shaders are now compiled and the only thing left to do is link both shader objects into a <def>shader program</def> that we can use for rendering. Make sure to check for compile errors here as well! +これでシェーダーはどちらもコンパイルできたので最後に、描画に利用する<def>シェーダープログラム</def>にふたつのシェーダーをリンクします。ここでもコンパイルエラーを確認してください。 +</p> + +<h3>Shader program</h3> +<h3>シェーダープログラム</h3> +<p> + A shader program object is the final linked version of multiple shaders combined. To use the recently compiled shaders we have to <def>link</def> them to a shader program object and then activate this shader program when rendering objects. The activated shader program's shaders will be used when we issue render calls. +シェーダープログラムオブジェクトは複数のシェーダーをリンクしたものです。今コンパイルしたシェーダーを利用するにはそれらをシェーダープログラムオブジェクトにリンクして描画時にこのプログラムをアクティベートする必要があります。アクティブなシェーダープログラムのシェーダーが描画命令を実行する際に利用されます。 +</p> + +<p> + When linking the shaders into a program it links the outputs of each shader to the inputs of the next shader. This is also where you'll get linking errors if your outputs and inputs do not match. </p> +シェーダーをひとつのプログラムとしてリンクする際、各シェーダーの出力が次のシェーダーの入力に繋がれます。この出力と入力が合致しないと、リンクエラーが発生します。 +<p> + Creating a program object is easy: +プログラムオブジェクトの作成は簡単です: +</p> + +<pre><code> +unsigned int shaderProgram; +shaderProgram = <function id='36'>glCreateProgram</function>(); +</code></pre> + +<p> + The <fun><function id='36'>glCreateProgram</function></fun> function creates a program and returns the ID reference to the newly created program object. Now we need to attach the previously compiled shaders to the program object and then link them with <fun><function id='35'>glLinkProgram</function></fun>: +<fun><function id='36'>glCreateProgram</function></fun>はプログラムを作成し、そのプログラムオブジェクトを参照するIDを返します。次に、先程コンパイルしたシェーダーを今作ったプログラムオブジェクトに関連付け、<fun><function id='35'>glLinkProgram</function></fun>によりリンクします: +</p> + +<pre><code> +<function id='34'>glAttachShader</function>(shaderProgram, vertexShader); +<function id='34'>glAttachShader</function>(shaderProgram, fragmentShader); +<function id='35'>glLinkProgram</function>(shaderProgram); +</code></pre> + +<p> + The code should be pretty self-explanatory, we attach the shaders to the program and link them via <fun><function id='35'>glLinkProgram</function></fun>. +このコードは見たままのことを行います。シェーダーをプログラムに関連付け、<fun><function id='35'>glLinkProgram</function></fun>によりリンクします。 +</p> + +<note> + Just like shader compilation we can also check if linking a shader program failed and retrieve the corresponding log. However, instead of using <fun><function id='39'>glGetShaderiv</function></fun> and <fun><function id='40'>glGetShaderInfoLog</function></fun> we now use: +シェーダーのコンパイルと同様、シェーダープログラムのリンクが成功したかどうか、そのログとともに確かめることができます。ただし今回使う関数は<fun><function id='39'>glGetShaderiv</function></fun>と<fun><function id='40'>glGetShaderInfoLog</function></fun>ではなく以下のものです: + +<pre class="cpp"><code> +<function id='41'>glGetProgramiv</function>(shaderProgram, GL_LINK_STATUS, &success); +if(!success) { + glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); + ... +} +</code></pre> +</note> + +<p> + The result is a program object that we can activate by calling <fun><function id='28'>glUseProgram</function></fun> with the newly created program object as its argument: +こうして得られたプログラムオブジェクトはそれ自身を<fun><function id='28'>glUseProgram</function></fun>の第一引数に渡すことでアクティベートできます: +</p> + +<pre><code> +<function id='28'>glUseProgram</function>(shaderProgram); +</code></pre> + +<p> + Every shader and rendering call after <fun><function id='28'>glUseProgram</function></fun> will now use this program object (and thus the shaders). +<fun><function id='28'>glUseProgram</function></fun>の呼び出し以降、シェーダーや描画命令はこのプログラムオブジェクトおよびシェーダーを利用します。 +</p> + +<p> + Oh yeah, and don't forget to delete the shader objects once we've linked them into the program object; we no longer need them anymore: +これでよし。プログラムオブジェクトにリンクし終ったシェーダーオブジェクトはもう必要ないので忘れずに削除してください: +</p> + +<pre><code> +<function id='46'>glDeleteShader</function>(vertexShader); +<function id='46'>glDeleteShader</function>(fragmentShader); +</code></pre> + +<p> + Right now we sent the input vertex data to the GPU and instructed the GPU how it should process the vertex data within a vertex and fragment shader. We're almost there, but not quite yet. OpenGL does not yet know how it should interpret the vertex data in memory and how it should connect the vertex data to the vertex shader's attributes. We'll be nice and tell OpenGL how to do that. +これで、頂点データをGPUに送信し、頂点シェーダーとフラグメントシェーダーにおいてそのデータの処理方法を指定できました。ゴールまであと一歩です。OpenGLはメモリ内の頂点データをどのように解釈し、頂点シェーダーの属性にどう対応させるかをまだ知りません。OpenGLにその方法を教えてあげましょう。 +</p> + +<h2>Linking Vertex Attributes</h2> +<h2>頂点属性のリンク</h2> +<p> + The vertex shader allows us to specify any input we want in the form of vertex attributes and while this allows for great flexibility, it does mean we have to manually specify what part of our input data goes to which vertex attribute in the vertex shader. This means we have to specify how OpenGL should interpret the vertex data before rendering. +頂点シェーダーに対して頂点属性をどのようなかたちで渡すかは自由です。そのため非常に柔軟性が高い一方、入力となるデータが頂点シェーダーにおいてどのように頂点属性に対応するのかを自分の手で設定しなければなりません。描画の前に、OpenGLがどのように頂点データを解釈するのか指定しなければならないということです。 +</p> + +<p> + Our vertex buffer data is formatted as follows: +われわれの頂点バッファデータは以下のような形式です: +</p> + +<img src="/img/getting-started/vertex_attribute_pointer.png" class="clean" alt="Vertex attribte pointer setup of OpenGL VBO"/> + + <ul> + <li>The position data is stored as 32-bit (4 byte) floating point values.</li> + <li>位置データは32ビット(4バイト)の浮動小数点数です。</li> + <li>Each position is composed of 3 of those values.</li> + <li>それぞれの位置データは3つの数値からなります。</li> + <li>There is no space (or other values) between each set of 3 values. The values are <def>tightly packed</def> in the array.</li> + <li>位置データどうしの間には隙間や他のデータはありません。数値はデータ列中で<def>稠密</def>です。</li> + <li>The first value in the data is at the beginning of the buffer.</li> + <li>データ中の一番目の数値はバッファの先頭に位置します。</li> + </ul> + +<p> + With this knowledge we can tell OpenGL how it should interpret the vertex data (per vertex attribute) using <fun><function id='30'>glVertexAttribPointer</function></fun>: +以上の情報を基にOpenGLが頂点データを(頂点属性ごとに)どのように解釈するか、<fun><function id='30'>glVertexAttribPointer</function></fun>により指定します: +</p> + +<pre class="cpp"><code> +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +</code></pre> + +<p> + The function <fun><function id='30'>glVertexAttribPointer</function></fun> has quite a few parameters so let's carefully walk through them: +<fun><function id='30'>glVertexAttribPointer</function></fun>はたくさんの引数を取るので丁寧に見ていきましょう: +</p> + + <ul> + <li>The first parameter specifies which vertex attribute we want to configure. Remember that we specified the location of the <var>position</var> vertex attribute in the vertex shader with <code>layout (location = 0)</code>. This sets the location of the vertex attribute to <code>0</code> and since we want to pass data to this vertex attribute, we pass in <code>0</code>.</li> + <li>最初の引数はどの頂点属性を設定するのかを指定します。頂点シェーダーにおいて<var>位置</var>属性を<code>layout (location = 0)</code>によって設定したのを思いだしてください。このとき頂点属性の位置を<code>0</code>にしており、この頂点属性にデータを渡したいので、このひとつめの引数は<code>0</code>にします。</li> + <li>The next argument specifies the size of the vertex attribute. The vertex attribute is a <code>vec3</code> so it is composed of <code>3</code> values.</li> + <li>次の引数は頂点属性のサイズです。今回の頂点属性は<code>vec3</code>であり<code>3</code>つの数値からなります。</li> + + <li>The third argument specifies the type of the data which is <var>GL_FLOAT</var> (a <code>vec*</code> in GLSL consists of floating point values).</li> + <li>三番目の引数ではデータの型を指定します。ここでは<var>GL_FLOAT</var>です(GLSLにおいて<code>vec*</code>は浮動小数点数からなります)。</li> + + <li>The next argument specifies if we want the data to be normalized. If we're inputting integer data types (int, byte) and we've set this to <var>GL_TRUE</var>, the integer data is normalized to <code>0</code> (or <code>-1</code> for signed data) and <code>1</code> when converted to float. This is not relevant for us so we'll leave this at <var>GL_FALSE</var>.</li> + <li>次の引数はデータを正規化するかどうか指定します。データ型が整数(intやbyte)であり、かつこの引数を<var>GL_TRUE</var>にした場合、整数のデータは浮動小数点数に変換される際に<code>0</code>(符号付きの場合は<code>-1</code>)と<code>1</code>の間に納まるように正規化されます。今回は関係ありませんので<var>GL_FALSE</var>にしておきます。</li> + + <li>The fifth argument is known as the <def>stride</def> and tells us the space between consecutive vertex attributes. Since the next set of position data is located exactly 3 times the size of a <code>float</code> away we specify that value as the stride. Note that since we know that the array is tightly packed (there is no space between the next vertex attribute value) we could've also specified the stride as <code>0</code> to let OpenGL determine the stride (this only works when values are tightly packed). Whenever we have more vertex attributes we have to carefully define the spacing between each vertex attribute but we'll get to see more examples of that later on.</li> + <li>五番目の引数は<def>ストライド</def>と呼ばれるもので、連続する二つの頂点属性間の距離をあらわします。ある位置データから次の位置データまではちょうど<code>float</code>三つ分あるので、その値を指定します。頂点データの配列は稠密(頂点属性どうしの間に隙間がない)ので、<code>0</code>を指定してOpenGLがストライドを決定するようにすることもできます(データが稠密であるときにのみ有効です)。より多くの頂点属性がある場合は頂点属性間の距離をより慎重に設定しなければなりません。そのような例は後で詳しく紹介します。</li> + <li>The last parameter is of type <code>void*</code> and thus requires that weird cast. This is the <def>offset</def> of where the position data begins in the buffer. Since the position data is at the start of the data array this value is just <code>0</code>. We will explore this parameter in more detail later on</li> + <li>最後の引数は<code>void*</code>型なのでこのように奇妙な変換が必要です。これはバッファにおける位置データの開始位置で<def>オフセット</def>と呼ばれます。位置データはデータ配列の頭に位置しますので今回この値は<code>0</code>です。この引数についても後で詳しく解説します。</li> + </ul> + + <note> +Each vertex attribute takes its data from memory managed by a VBO and which VBO it takes its data from (you can have multiple VBOs) is determined by the VBO currently bound to <var>GL_ARRAY_BUFFER</var> when calling <fun><function id='30'>glVertexAttribPointer</function></fun>. Since the previously defined <var>VBO</var> is still bound before calling <fun><function id='30'>glVertexAttribPointer</function></fun> vertex attribute <code>0</code> is now associated with its vertex data. +頂点属性のデータはVBOが管理するメモリから読まれます。VBOは複数定義することができますが、<fun><function id='30'>glVertexAttribPointer</function></fun>を呼び出したときデータを読むのは現在<var>GL_ARRAY_BUFFER</var>に紐付いているVBOからです。以前定義したVBOが<fun><function id='30'>glVertexAttribPointer</function></fun>を呼び出す時点でまだ紐付いているので、頂点属性<code>0</code>はその頂点データに対応します。 +</note> + + +<p> + Now that we specified how OpenGL should interpret the vertex data we should also enable the vertex attribute with <fun><function id='29'><function id='60'>glEnable</function>VertexAttribArray</function></fun> giving the vertex attribute location as its argument; vertex attributes are disabled by default. From that point on we have everything set up: we initialized the vertex data in a buffer using a vertex buffer object, set up a vertex and fragment shader and told OpenGL how to link the vertex data to the vertex shader's vertex attributes. Drawing an object in OpenGL would now look something like this: +頂点データの解釈方法を設定したので、こんどは<fun><function id='29'><function id='60'>glEnable</function>VertexAttribArray</function></fun>を、その引数に頂点属性の位置を渡して呼び出すことにより頂点属性を有効化します(頂点属性はデフォルトで無効です)。これですべてのセットアップが完了しました: 頂点データを頂点バッファオブジェクトを用いてバッファ上に初期化し、頂点シェーダーとフラグメントシェーダーをセットアップし、頂点データと頂点シェーダー上の頂点属性との対応付けを指定しました。以上をまとめると、OpenGLでの描画は以下のようになります: +</p> + +<pre><code> +// 0. copy our vertices array in a buffer for OpenGL to use +// 0. 頂点の配列をOpenGLが利用できるようにバッファにコピー +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); +// 1. then set the vertex attributes pointers +// 1. 頂点属性のポインタをセット +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +// 2. use our shader program when we want to render an object +// 2. 作成したシェーダープログラムを描画に利用 +<function id='28'>glUseProgram</function>(shaderProgram); +// 3. now draw the object +// 3. 描画 +someOpenGLFunctionThatDrawsOurTriangle(); +</code></pre> + +<p> + We have to repeat this process every time we want to draw an object. It may not look like that much, but imagine if we have over 5 vertex attributes and perhaps 100s of different objects (which is not uncommon). Binding the appropriate buffer objects and configuring all vertex attributes for each of those objects quickly becomes a cumbersome process. What if there was some way we could store all these state configurations into an object and simply bind this object to restore its state? +この作業を描画のたびに繰返す必要があります。現状そんなに大変そうに思えませんが、これが例えば5つの頂点属性と100種類のオプジェクトだったらどうでしょう。この状況はそんなに有り得ないものではありません。バッファオブジェクトと適切に紐付け、すべての頂点属性ををれぞれのオブジェクトで設定するのは面倒です。もしこのような設定を保存しておけるオブジェクトがあり、それを紐付けるだけで保存した設定を読み込めたらどんなに楽でしょう。 +</p> + +<h3>Vertex Array Object</h3> +<h3>頂点配列オブジェクト</h3> +<p> + A <def>vertex array object</def> (also known as <def>VAO</def>) can be bound just like a vertex buffer object and any subsequent vertex attribute calls from that point on will be stored inside the VAO. This has the advantage that when configuring vertex attribute pointers you only have to make those calls once and whenever we want to draw the object, we can just bind the corresponding VAO. This makes switching between different vertex data and attribute configurations as easy as binding a different VAO. All the state we just set is stored inside the VAO. +<def>頂点配列オブジェクト</def>(あるいは<def>VAO</def>)は頂点バッファオブジェクトと同様に紐付けることができ、それ以降の操作はそのVAOの内部に保存されます。これには大きな利点があります。頂点属性のポインターの設定を一度だけしておけば描画時には対応するVAOを紐付けるだけですむのです。別の頂点データと属性の設定を行き来する場合も、VAOの紐付けだけで完結するので簡単です。設定したすべての状態がVAO内に保存されます。 +</p> + +<warning> + Core OpenGL <strong>requires</strong> that we use a VAO so it knows what to do with our vertex inputs. If we fail to bind a VAO, OpenGL will most likely refuse to draw anything. +core OpenGLを利用する場合、OpenGLに頂点データの扱い方を指示するためにVAOが<strong>必要</strong>です。VAOの紐付けに失敗した場合、OpenGLはおそらく一切の描画を拒否します。 +</warning> + +<p> + A vertex array object stores the following: +VAOは以下のものを保持します: +</p> + +<ul> + <li>Calls to <fun><function id='29'><function id='60'>glEnable</function>VertexAttribArray</function></fun> or <fun>glDisableVertexAttribArray</fun>.</li> + <li><fun><function id='29'><function id='60'>glEnable</function>VertexAttribArray</function></fun>や<fun>glDisableVertexAttribArray</fun>の呼び出し。</li> + <li>Vertex attribute configurations via <fun><function id='30'>glVertexAttribPointer</function></fun>.</li> + <li><fun><function id='30'>glVertexAttribPointer</function></fun>による頂点属性の設定。</li> + <li>Vertex buffer objects associated with vertex attributes by calls to <fun><function id='30'>glVertexAttribPointer</function></fun>.</li> + <li><fun><function id='30'>glVertexAttribPointer</function></fun>により頂点属性に紐付いた頂点バッファオブジェクト。</li> +</ul> + + <img src="/img/getting-started/vertex_array_objects.png" class="clean" alt="Image of how a VAO (Vertex Array Object) operates and what it stores in OpenGL"/> + +<p> + The process to generate a VAO looks similar to that of a VBO: +VAOの作成はVBOと同様です: +</p> + +<pre class="cpp"><code> +unsigned int VAO; +<function id='33'>glGenVertexArrays</function>(1, &amp;VAO); +</code></pre> + +<p> + To use a VAO all you have to do is bind the VAO using <fun><function id='27'>glBindVertexArray</function></fun>. From that point on we should bind/configure the corresponding VBO(s) and attribute pointer(s) and then unbind the VAO for later use. As soon as we want to draw an object, we simply bind the VAO with the preferred settings before drawing the object and that is it. In code this would look a bit like this: +VAOを利用するために必要なのは<fun><function id='27'>glBindVertexArray</function></fun>によりVAOを紐付けることだけです。この後、対応するVBOや属性ポインタの紐付けや設定を行い、後々の利用にそなえてVAOの紐付けを解除します。ある物体を描画したい場合、描画の前にその物体の設定を保持したVAOを紐付けるだけで十分です。以上をコードに落し込むとこのようになります: +</p> + +<pre><code> +// ..:: Initialization code (done once (unless your object frequently changes)) :: .. +// ..:: 初期化のコード(描画する物体を頻繁に変更しないのであれば一度だけ)::.. +// 1. bind Vertex Array Object +// 1. VAOを紐付け +<function id='27'>glBindVertexArray</function>(VAO); +// 2. copy our vertices array in a buffer for OpenGL to use +// 2. 頂点配列をバッファにコピー +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); +// 3. then set our vertex attributes pointers +// 3. 頂点属性のポインタを設定 +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); + + +[...] + +// ..:: Drawing code (in render loop) :: .. +// ..:: 描画命令(描画ループ内)::.. +// 4. draw the object +// 4. 描画 +<function id='28'>glUseProgram</function>(shaderProgram); +<function id='27'>glBindVertexArray</function>(VAO); +someOpenGLFunctionThatDrawsOurTriangle(); +</code></pre> + +<p> + And that is it! Everything we did the last few million pages led up to this moment, a VAO that stores our vertex attribute configuration and which VBO to use. Usually when you have multiple objects you want to draw, you first generate/configure all the VAOs (and thus the required VBO and attribute pointers) and store those for later use. The moment we want to draw one of our objects, we take the corresponding VAO, bind it, then draw the object and unbind the VAO again. +やりました。ここまでの数千ページに及ぶ努力がこの瞬間実を結びました。VAOが頂点属性の設定や描画に利用するVBOの情報を保持しています。複数の物体を描画したい場合は通常、まずそれぞれの物体に対してVAO(とそれに必要なVBOや属性のポインタ)を作成、設定し利用に供えて保存しておきます。そして描画が必要になった時、描画したい物体のVAOを紐付け、描画し、最後に紐付けを解除します。 +</p> + +<h3>The triangle we've all been waiting for</h3> +<h3>待ち侘びた三角形</h3> +<p> + To draw our objects of choice, OpenGL provides us with the <fun><function id='1'>glDrawArrays</function></fun> function that draws primitives using the currently active shader, the previously defined vertex attribute configuration and with the VBO's vertex data (indirectly bound via the VAO). +好きなものを描画するために、OpenGLには<fun><function id='1'>glDrawArrays</function></fun>という関数が用意されています。この関数は、現在有効化されているシェーダー、以前定義した頂点属性の設定およびVBO(VAOを通して間接的に紐付けられたもの)を用いてプリミティブ(基本図形)を描きます。 +</p> + +<pre class="cpp"><code> +<function id='28'>glUseProgram</function>(shaderProgram); +<function id='27'>glBindVertexArray</function>(VAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 3); +</code></pre> + +<p> + The <fun><function id='1'>glDrawArrays</function></fun> function takes as its first argument the OpenGL primitive type we would like to draw. Since I said at the start we wanted to draw a triangle, and I don't like lying to you, we pass in <var>GL_TRIANGLES</var>. The second argument specifies the starting index of the vertex array we'd like to draw; we just leave this at <code>0</code>. The last argument specifies how many vertices we want to draw, which is <code>3</code> (we only render 1 triangle from our data, which is exactly 3 vertices long). +<fun><function id='1'>glDrawArrays</function></fun>はひとつ目の引数に描きたいOpenGLのプリミティブをとります。冒頭で三角形を描画すると言い、嘘をつきたくないので、ここでは<var>GL_TRIANGLES</var>を渡します。ふたつ目の引数は頂点配列の何番目の頂点から描きはじめるかを指定します。ここでは<code>0</code>にしておきましょう。最後の引数はいくつの頂点を描くかを指定します。ひとつの三角形を描きたいので<code>3</code>を渡します。 +</p> + +<p> + Now try to compile the code and work your way backwards if any errors popped up. As soon as your application compiles, you should see the following result: +それではコンパイルしてください。なにかエラーがでれば戻って確認してください。コンパイルが完了すれば、以下のような結果が見えるはずです。 +</p> + +<img src="/img/getting-started/hellotriangle.png" width="600px" class="clean" alt="An image of a basic triangle rendered in modern OpenGL" /> + +<p> + The source code for the complete program can be found <a href="/code_viewer_gh.php?code=src/1.getting_started/2.1.hello_triangle/hello_triangle.cpp" target="_blank">here</a> . +完全なソースコードは<a href="/code_viewer_gh.php?code=src/1.getting_started/2.1.hello_triangle/hello_triangle.cpp" target="_blank">ここ</a>から確認できます。 +</p> + +<p> + If your output does not look the same you probably did something wrong along the way so check the complete source code and see if you missed anything. +なにか別のものが表示されたのであれば、途中でなにか間違えているはずですので、完全なソースコードと比較してください。 +</p> + +<h2> Element Buffer Objects </h2> +<h2>要素バッファオブジェクト</h2> +<p> + There is one last thing we'd like to discuss when rendering vertices and that is <def>element buffer objects</def> abbreviated to EBO. To explain how element buffer objects work it's best to give an example: suppose we want to draw a rectangle instead of a triangle. We can draw a rectangle using two triangles (OpenGL mainly works with triangles). This will generate the following set of vertices: +頂点を描画するにあたり紹介しておきたいものがもうひとつあります。EBOと略記される<def>要素バッファオブジェクト</def>です。例をあげて説明します。今、三角形ではなく四角形を描画したいとしましょう。OpenGLは主に三角形を描画するように作られているので、この四角形は二つの三角形として描くことになります。そのため以下のような頂点を作成することになります: +</p> + +<pre><code> +float vertices[] = { + // first triangle + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, 0.5f, 0.0f, // top left + // second triangle + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left +}; +</code></pre> + +<p> + As you can see, there is some overlap on the vertices specified. We specify <code>bottom right</code> and <code>top left</code> twice! This is an overhead of 50% since the same rectangle could also be specified with only 4 vertices, instead of 6. This will only get worse as soon as we have more complex models that have over 1000s of triangles where there will be large chunks that overlap. What would be a better solution is to store only the unique vertices and then specify the order at which we want to draw these vertices in. In that case we would only have to store 4 vertices for the rectangle, and then just specify at which order we'd like to draw them. Wouldn't it be great if OpenGL provided us with a feature like that? +ご覧のように定義した頂点に重複があります。右下と左上が二回でてきます。四角形は6つではなく4つの頂点があれば記述できるのでこれは50%も無駄なことをしていることになります。これが例えば1000個以上の三角形からなる複雑なモデルの場合、さらにたくさんの重複が生じ、状況は悪くなる一方です。解決策としては各頂点を重複がないように保存し、頂点を描く順番をあとから指定する方法が考えられます。この場合四角形を描くために頂点を4つだけ保存し、どのような順番で描くのか指定すればいいのです。このような方法がOpenGLに備わっていたら嬉しいと思いませんか? +</p> + +<p> + Thankfully, element buffer objects work exactly like that. An EBO is a buffer, just like a vertex buffer object, that stores indices that OpenGL uses to decide what vertices to draw. This so called <def>indexed drawing</def> is exactly the solution to our problem. To get started we first have to specify the (unique) vertices and the indices to draw them as a rectangle: +ありがたいことに、EBOがその役割を担ってくれます。EBOは頂点バッファオブジェクト同様バッファであり、OpenGLがどの頂点を描くのかを決定するためのインデックスを保持します。この、<def>インデックスによる描画</def>と呼ばれる方法がわれわれの問題を解決してくれます。これを利用するためにはまず、頂点を重複なく指定し、インデックスを作成します: +</p> + +<pre><code> +float vertices[] = { + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left +}; +unsigned int indices[] = { // note that we start from 0! + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle +}; +</code></pre> + +<p> + You can see that, when using indices, we only need 4 vertices instead of 6. Next we need to create the element buffer object: +ご覧のようにインデックスを利用すれば必要な頂点は6個ではなく4個だけになります。次にEBOを作成します: +</p> + +<pre class="cpp"><code> +unsigned int EBO; +<function id='12'>glGenBuffers</function>(1, &amp;EBO); +</code></pre> + +<p> + Similar to the VBO we bind the EBO and copy the indices into the buffer with <fun><function id='31'>glBufferData</function></fun>. Also, just like the VBO we want to place those calls between a bind and an unbind call, although this time we specify <var>GL_ELEMENT_ARRAY_BUFFER</var> as the buffer type. +VBOと同じく、EBOを紐付け<fun><function id='31'>glBufferData</function></fun>によりインデックスをそのバッファにコピーします。さらにVBOと同様、これらの関数の呼び出しは紐付けた後、それを解除するまでの間に行います。ただし今回はバッファの種類として<var>GL_ELEMENT_ARRAY_BUFFER</var>を指定します。 +</p> + +<pre><code> +<function id='32'>glBindBuffer</function>(GL_ELEMENT_ARRAY_BUFFER, EBO); +<function id='31'>glBufferData</function>(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); +</code></pre> + +<p> + Note that we're now giving <var>GL_ELEMENT_ARRAY_BUFFER</var> as the buffer target. The last thing left to do is replace the <fun><function id='1'>glDrawArrays</function></fun> call with <fun><function id='2'>glDrawElements</function></fun> to indicate we want to render the triangles from an index buffer. When using <fun><function id='2'>glDrawElements</function></fun> we're going to draw using indices provided in the element buffer object currently bound: +今回は紐付けるバッファとして<var>GL_ELEMENT_ARRAY_BUFFER</var>を指定していることに注意してください。あとはバッファオブジェクトから三角形を描画するために<fun><function id='1'>glDrawArrays</function></fun>を<fun><function id='2'>glDrawElements</function></fun>で置きかえることだけです。<fun><function id='2'>glDrawElements</function></fun>を利用することで、現在紐付いているEBOのインデックスにより描画できます: +</p> + +<pre class="cpp"><code> +<function id='32'>glBindBuffer</function>(GL_ELEMENT_ARRAY_BUFFER, EBO); +<function id='2'>glDrawElements</function>(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +</code></pre> + +<p> + The first argument specifies the mode we want to draw in, similar to <fun><function id='1'>glDrawArrays</function></fun>. The second argument is the count or number of elements we'd like to draw. We specified 6 indices so we want to draw 6 vertices in total. The third argument is the type of the indices which is of type <var>GL_UNSIGNED_INT</var>. The last argument allows us to specify an offset in the EBO (or pass in an index array, but that is when you're not using element buffer objects), but we're just going to leave this at 0. +最初の引数は<fun><function id='1'>glDrawArrays</function></fun>と同様に描画するモードを指定します。二つ目の引数には描画する要素の数を渡します。6つのインデックスを指定したので6つの頂点を描画します。三番目はインデックスの型で、ここでは<var>GL_UNSIGNED_INT</var>です。最後の引数ではEBOの中でのデータの開始位置を指定できます(あるいはEBOを利用しない場合はインデックスへのポインタ)。今回は0のままにしておきます。 +</p> + +<p> + The <fun><function id='2'>glDrawElements</function></fun> function takes its indices from the EBO currently bound to the <var>GL_ELEMENT_ARRAY_BUFFER</var> target. This means we have to bind the corresponding EBO each time we want to render an object with indices which again is a bit cumbersome. It just so happens that a vertex array object also keeps track of element buffer object bindings. The last element buffer object that gets bound while a VAO is bound, is stored as the VAO's element buffer object. Binding to a VAO then also automatically binds that EBO. +<fun><function id='2'>glDrawElements</function></fun>はインデックスを現在<var>GL_ELEMENT_ARRAY_BUFFER</var>に紐付いているEBOからとります。つまり、インデックスを用いてなにか描画する際には、いちいちEBOを紐付ける必要があるということです。これも少し面倒です。しかしVAOがEBOの紐付けも追跡しているのでこの面倒は回避できます。VAOが紐付いている状態で最後に紐付けられたEBOを、VAOは記憶しています。VAOの紐付けによりEBOも自動で紐付くようになっているのです。 +</p> + +<img src="/img/getting-started/vertex_array_objects_ebo.png" class="clean" alt="Image of VAO's structure / what it stores now also with EBO bindings."/> + +<warning> + A VAO stores the <fun><function id='32'>glBindBuffer</function></fun> calls when the target is <var>GL_ELEMENT_ARRAY_BUFFER</var>. This also means it stores its unbind calls so make sure you don't unbind the element array buffer before unbinding your VAO, otherwise it doesn't have an EBO configured. +VAOは、<fun><function id='32'>glBindBuffer</function></fun>のターゲットが<var>GL_ELEMENT_ARRAY_BUFFER</var>の場合、その呼び出しを記憶しています。つまりVAOはEBOの紐付けの解除も記憶してしまうので、VAOの紐付けを解除するまえにEBOを解除しないようにしてください。そうしないとこのVAOがEBOの設定を破棄してしまいます。 +</warning> + +<p> + The resulting initialization and drawing code now looks something like this: +以上を踏まえると、初期化のコードは以下のようになります: +</p> + +<pre><code> +// ..:: Initialization code :: .. +// ..:: 初期化のコード ::.. +// 1. bind Vertex Array Object +// 1. VAOの紐付け +<function id='27'>glBindVertexArray</function>(VAO); +// 2. copy our vertices array in a vertex buffer for OpenGL to use +// 2. 頂点配列を頂点バッファにコピー +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); +// 3. copy our index array in a element buffer for OpenGL to use +// 3. インデックス配列をEBOにコピー +<function id='32'>glBindBuffer</function>(GL_ELEMENT_ARRAY_BUFFER, EBO); +<function id='31'>glBufferData</function>(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); +// 4. then set the vertex attributes pointers +// 4. 頂点属性のポインタをセット +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); + +[...] + +// ..:: Drawing code (in render loop) :: .. +// ..:: 描画命令(描画ループ内)::.. +<function id='28'>glUseProgram</function>(shaderProgram); +<function id='27'>glBindVertexArray</function>(VAO); +<function id='2'>glDrawElements</function>(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) +<function id='27'>glBindVertexArray</function>(0); +</code></pre> + +<p> + Running the program should give an image as depicted below. The left image should look familiar and the right image is the rectangle drawn in <def>wireframe mode</def>. The wireframe rectangle shows that the rectangle indeed consists of two triangles. +このプログラムを実行すると以下のような画像が出力されます。左の画像はこのプログラムが表示するものですが、右は<def>ワイヤーフレームモード</def>で描画したものです。ワーヤーフレームで表示した四角形は実際には二つの三角形で成り立っていることが分かります。 +</p> + +<img src="/img/getting-started/hellotriangle2.png" width="800px" class="clean" alt="A rectangle drawn using indexed rendering in OpenGL"/> + +<note> + <strong>Wireframe mode</strong><br/> + <strong>ワイヤーフレームモード</strong><br/> + To draw your triangles in wireframe mode, you can configure how OpenGL draws its primitives via <code><function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_LINE)</code>. The first argument says we want to apply it to the front and back of all triangles and the second line tells us to draw them as lines. Any subsequent drawing calls will render the triangles in wireframe mode until we set it back to its default using <code><function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_FILL)</code>. +三角形をワイヤーフレームモードで表示することは、OpenGLがどのようにプリミティブを表示するかを<code><function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_LINE)</code>により指定することで可能です。最初の引数で三角形の前面と後面の両方に適応することを指定します。二つ目の引数は三角形の描画に線を用いることを指定します。以降のすべての描画命令では三角形をワイヤーフレームモードで描画します。もとに戻すには<code><function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_FILL)</code>を実行します。 +</note> + +<p> + If you have any errors, work your way backwards and see if you missed anything. You can find the complete source code <a href="/code_viewer_gh.php?code=src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp" target="_blank">here</a>. +もしエラーがでたら来た道を戻ってミスがないか確認してください。完全なソースコードは<a href="/code_viewer_gh.php?code=src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp" target="_blank">ここ</a>にあります。 +</p> + +<p> + If you managed to draw a triangle or a rectangle just like we did then congratulations, you managed to make it past one of the hardest parts of modern OpenGL: drawing your first triangle. This is a difficult part since there is a large chunk of knowledge required before being able to draw your first triangle. Thankfully, we now made it past that barrier and the upcoming chapters will hopefully be much easier to understand. +三角形や四角形を描画できた人は、おめでとうございます。あなたはOpenGLの学習で最も困難な部分を完了しました。最初の三角形を描画できたのです。最初の三角形を描画するためには多くの知識が必要なのです。その障壁を突破できたあなたは、以降の節を簡単に理解できるでしょう。 +</p> + +<h2>Additional resources</h2> +<h2>参考</h2> +<ul> + <li><a href="http://antongerdelan.net/opengl/hellotriangle.html" target="_blank">antongerdelan.net/hellotriangle</a>: Anton Gerdelan's take on rendering the first triangle.</li> + <li><a href="http://antongerdelan.net/opengl/hellotriangle.html" target="_blank">antongerdelan.net/hellotriangle</a>: Anton Gerdelanによる最初の三角形のチュートリアル。</li> + <li><a href="https://open.gl/drawing" target="_blank">open.gl/drawing</a>: Alexander Overvoorde's take on rendering the first triangle.</li> + <li><a href="https://open.gl/drawing" target="_blank">open.gl/drawing</a>: Alexander Overvoordeによる最初の三角形のチュートリアル。 </li> + <li><a href="http://antongerdelan.net/opengl/vertexbuffers.html" target="_blank">antongerdelan.net/vertexbuffers</a>: some extra insights into vertex buffer objects.</li> + <li><a href="http://antongerdelan.net/opengl/vertexbuffers.html" target="_blank">antongerdelan.net/vertexbuffers</a>: VBOに関するさらなる情報。</li> + <li><a href="https://learnopengl.com/In-Practice/Debugging" target="_blank">learnopengl.com/In-Practice/Debugging</a>: there are a lot of steps involved in this chapter; if you're stuck it may be worthwhile to read a bit on debugging in OpenGL (up until the debug output section).</li> + <li><a href="https://learnopengl.com/In-Practice/Debugging" target="_blank">learnopengl.com/In-Practice/Debugging</a>: この節にはたくさんのステップがありました。どこかで嵌った場合、OpenGLのデバッグについて少し確認するのがいいでしょう(デバッグ情報の出力の節まで読んでみてください)。</li> +</ul> + +<h1>Exercises</h1> +<h1>演習</h1> +<p> + To really get a good grasp of the concepts discussed a few exercises were set up. It is advised to work through them before continuing to the next subject to make sure you get a good grasp of what's going on. +ここまで説明してきた概念をより深く理解するために演習問題を用意しました。なにが起こっているのかきちんと理解するために、次の話題に進む前にこの演習問題を解くことをおすすめします。 +</p> + +<ol> + <li>Try to draw 2 triangles next to each other using <fun><function id='1'>glDrawArrays</function></fun> by adding more vertices to your data: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.3.hello_triangle_exercise1/hello_triangle_exercise1.cpp" target="_blank">solution</a>.</li> + <li>頂点を追加し、<fun><function id='1'>glDrawArrays</function></fun>を使って二つの三角形を隣り合わせに描いてください: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.3.hello_triangle_exercise1/hello_triangle_exercise1.cpp" target="_blank">回答</a>。</li> + <li>Now create the same 2 triangles using two different VAOs and VBOs for their data: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.4.hello_triangle_exercise2/hello_triangle_exercise2.cpp" target="_blank">solution</a>.</li> + <li>同じく二つの三角形を、それぞれのVAOとVBOを用意して描いてください: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.4.hello_triangle_exercise2/hello_triangle_exercise2.cpp" target="_blank">回答</a>。</li> + <li>Create two shader programs where the second program uses a different fragment shader that outputs the color yellow; draw both triangles again where one outputs the color yellow: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.5.hello_triangle_exercise3/hello_triangle_exercise3.cpp" target="_blank">solution</a>.</li> + <li>二つのシェーダープログラムを作成し、二つ目のプログラムのフラグメントシェーダーは黄色を出力するようにしてください。そして今回も二つの三角形を、片方は黄色になるように描いてください。: <a href="/code_viewer_gh.php?code=src/1.getting_started/2.5.hello_triangle_exercise3/hello_triangle_exercise3.cpp" target="_blank">回答</a>.</li> +</ol> + + + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Hello-Window.html b/pub/Getting-started/Hello-Window.html @@ -0,0 +1,634 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Hello Window</h1> + <h1 id="content-title">はじめてのウィンドウ</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Hello-Window</h1> +<p> + Let's see if we can get GLFW up and running. First, create a <code>.cpp</code> file and add the following includes to the top of your newly created file. +GLFWを立ち上げ実行できるかどうか確認しましょう。はじめに<code>.cpp</code>ファイルを作成し、一番上に以下のインクルードディレクティブを書いてください: +</p> + +<pre><code> +#include &lt;glad/glad.h&gt; +#include &lt;GLFW/glfw3.h&gt; +</code></pre>。 + +<warning> + Be sure to include GLAD before GLFW. The include file for GLAD includes the required OpenGL headers behind the scenes (like <code>GL/gl.h</code>) so be sure to include GLAD before other header files that require OpenGL (like GLFW). +必ずGLADをGLFWより前にインクルードしてください。GLADをインクルードすると、必要なOpenGLのヘッダーファイルを一緒にインクルードしてくれます(たとえば<code>GL/gl.h</code>のようなものです)。そのためGLFWのようなOpenGLを必要とするヘッダーのまえに、GLADをインクルードする必要があるのです。 +</warning> + +<p> + Next, we create the <fun>main</fun> function where we will instantiate the GLFW window: +続いて<fun>main</fun>関数を作成し、GLFWをインスタンス化します: +</p> + +<pre><code> +int main() +{ + <function id='17'>glfwInit</function>(); + <function id='18'>glfwWindowHint</function>(GLFW_CONTEXT_VERSION_MAJOR, 3); + <function id='18'>glfwWindowHint</function>(GLFW_CONTEXT_VERSION_MINOR, 3); + <function id='18'>glfwWindowHint</function>(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + //<function id='18'>glfwWindowHint</function>(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + return 0; +} +</code></pre>。 + +<p> + In the main function we first initialize GLFW with <fun><function id='17'>glfwInit</function></fun>, after which we can configure GLFW using <fun><function id='18'>glfwWindowHint</function></fun>. The first argument of <fun><function id='18'>glfwWindowHint</function></fun> tells us what option we want to configure, where we can select the option from a large enum of possible options prefixed with <code>GLFW_</code>. The second argument is an integer that sets the value of our option. A list of all the possible options and its corresponding values can be found at <a href="http://www.glfw.org/docs/latest/window.html#window_hints" target="_blank">GLFW's window handling</a> documentation. If you try to run the application now and it gives a lot of <em>undefined reference</em> errors it means you didn't successfully link the GLFW library. +main関数のはじめに<fun><function id='17'>glfwInit</function></fun>を使ってGLFWを初期化します。続いて<fun><function id='18'>glfwWindowHint</function></fun>によりGLFWの設定を行います。<fun><function id='18'>glfwWindowHint</function></fun>のひとつめの変数は設定したい項目で、頭に<code>GLFW_</code>がついた大きなenumのなかから選べます。ふたつめの変数は選んだオプションを設定する整数です。利用可能なオプションと対応する整数値は<a href="http://www.glfw.org/docs/latest/window.html#window_hints" target="_blank">GLFW's window handling</a>において確認できます。この時点でアプリケーションを実行して大量の<em>undefined reference</em>がでた場合、GLFWライブラリをうまくリンクできていないということです。 +</p> + +<p> + Since the focus of this book is on OpenGL version 3.3 we'd like to tell GLFW that 3.3 is the OpenGL version we want to use. This way GLFW can make the proper arrangements when creating the OpenGL context. This ensures that when a user does not have the proper OpenGL version GLFW fails to run. We set the major and minor version both to <code>3</code>. We also tell GLFW we want to explicitly use the core-profile. Telling GLFW we want to use the core-profile means we'll get access to a smaller subset of OpenGL features without backwards-compatible features we no longer need. Note that on Mac OS X you need to add <code><function id='18'>glfwWindowHint</function>(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);</code> to your initialization code for it to work. +われわれはOpenGLのversion 3.3を利用したいので、GLFWにそのことを伝えます。こうすることで、OpenGLコンテクストを作成するにあたりGLFWが適切な変数を作成できるようになります。また、ユーザーのコンピュータにOpenGLの適切なバージョンが入っていない場合、GLFWの実行が失敗することが保証されます。メジャーバージョンとマイナーバージョンの両方を3に設定しましょう。さらに、core-profileを利用することを明記します。こうすることでわれわれには必要のない下位互換性を捨て、OpenGLをコンパクトに利用できます。あなたがMac OS Xを利用している場合、<code><function id='18'>glfwWindowHint</function>(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);</code>を追加する必要があります。 +</p> + +<note> + Make sure you have OpenGL versions 3.3 or higher installed on your system/hardware otherwise the application will crash or display undefined behavior. To find the OpenGL version on your machine either call <strong>glxinfo</strong> on Linux machines or use a utility like the <a href="http://download.cnet.com/OpenGL-Extensions-Viewer/3000-18487_4-34442.html" target="_blank">OpenGL Extension Viewer</a> for Windows. If your supported version is lower try to check if your video card supports OpenGL 3.3+ (otherwise it's really old) and/or update your drivers. +あなたのシステム/ハードウェアにOpenGLバージョン3.3より新しいものがインストールされていることを確認してください。さもなければアプリケーションがクラッシュしたり、不定義の動作をすることがあります。コンピュータにインストールされたOpenGLのバージョンを調べるには、Linuxにおいては<strong>glxinfo</strong>を、Windowsにおいては<a href="http://download.cnet.com/OpenGL-Extensions-Viewer/3000-18487_4-34442.html" target="_blank">OpenGL Extension Viewer</a>のようなものを利用してください。OpenGLのバージョンが古い場合、ビデオカードがOpenGLの3.3以上をサポートしていないか確認し、ドライバをアップデートしてください(サポートしていないビデオカードは相当古いです)。 +</note> + +<p> + Next we're required to create a window object. This window object holds all the windowing data and is required by most of GLFW's other functions. +次はウィンドウオブジェクトの作成です。ウィンドウオブジェクトはウィンドウにかかるすべてのデータを保持しており、GLFWのほとんどの関数を利用するうえで必要となります。 +</p> + +<pre><code> +GLFWwindow* window = <function id='20'>glfwCreateWindow</function>(800, 600, "LearnOpenGL", NULL, NULL); +if (window == NULL) +{ + std::cout &lt;&lt; "Failed to create GLFW window" &lt;&lt; std::endl; + <function id='25'>glfwTerminate</function>(); + return -1; +} +<function id='19'>glfwMakeContextCurrent</function>(window); +</code></pre> + +<p> + The <fun><function id='20'>glfwCreateWindow</function></fun> function requires the window width and height as its first two arguments respectively. The third argument allows us to create a name for the window; for now we call it <code>&quot;LearnOpenGL&quot;</code> but you're allowed to name it however you like. We can ignore the last 2 parameters. The function returns a <fun>GLFWwindow</fun> object that we'll later need for other GLFW operations. After that we tell GLFW to make the context of our window the main context on the current thread. +<fun><function id='20'>glfwCreateWindow</function></fun>関数はウィンドウの幅と高さをそれぞれ第一および第二引数としてとります。三番目の引数はウィンドウの名前です。ここでは<code>&quot;LearnOpenGL&quot;</code>としていますが、あなたの好きな名前でかまいません。あとのふたつの引数はここでは無視します。この関数は<fun>GLFWwindow</fun>オブジェクトを返します。このオブジェクトはGLFWでほかの操作をするときに必要なものです。ウィンドウオブジェクトを作成したあとは、コンテクストの作成です。先程作ったウィンドウのコンテクストをこれから利用するコンテクストとして宣言します。 +</p> + +<h2>GLAD</h2> +<p> + In the previous chapter we mentioned that GLAD manages function pointers for OpenGL so we want to initialize GLAD before we call any OpenGL function: +前の章でGLADが関数のポインタを制御するといいました。OpenGLの関数を呼びだすまえにGLADを初期化する方法を見ていきましょう。 +</p> + +<pre><code> +if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) +{ + std::cout &lt;&lt; "Failed to initialize GLAD" &lt;&lt; std::endl; + return -1; +} +</code></pre> + +<p> + We pass GLAD the function to load the address of the OpenGL function pointers which is OS-specific. GLFW gives us <fun>glfwGetProcAddress</fun> that defines the correct function based on which OS we're compiling for. +GLADを上記の関数で処理することにより、OpenGLの関数へのポインタのアドレスをロードします。GLFWは<fun>glfwGetProcAddress</fun>により利用しているOSにあった関数を正確に定義します。 +</p> + +<h2>Viewport</h2> +<h2>ビューポート</h2> +<p> + Before we can start rendering we have to do one last thing. We have to tell OpenGL the size of the rendering window so OpenGL knows how we want to display the data and coordinates with respect to the window. We can set those <em>dimensions</em> via the <fun><function id='22'>glViewport</function></fun> function: +描画をはじめる前に、もうひとつしておかなければならないことがあります。OpenGLに描画するウィンドウのサイズを伝え、ウィンドウ中にデータと座標をどのように表示させるかを決定することです。<fun><function id='22'>glViewport</function></fun>により画面の<em>寸法</em>を設定できます: +</p> + +<pre class="cpp"><code> +<function id='22'>glViewport</function>(0, 0, 800, 600); +</code></pre> + +<p> + The first two parameters of <fun><function id='22'>glViewport</function></fun> set the location of the lower left corner of the window. The third and fourth parameter set the width and height of the rendering window in pixels, which we set equal to GLFW's window size. +<fun><function id='22'>glViewport</function></fun>のはじめのふたつの引数はウィンドウ左下の角の場所です。あとのふたつの引数で、ウィンドの幅と高さをピクセル単位で指定します。ここではGLFWのウィンドウと同じサイズにしましょう。 +</p> + +<p> + We could actually set the viewport dimensions at values smaller than GLFW's dimensions; then all the OpenGL rendering would be displayed in a smaller window and we could for example display other elements outside the OpenGL viewport. +ビューポートの寸法をGLFWのものより小さくすることも可能です。そうすることで、OpenGLによる描画がウィンドウより小さくなり、あいたところに他のものを配置したりすることも可能になります。 +</p> + +<note> + Behind the scenes OpenGL uses the data specified via <fun><function id='22'>glViewport</function></fun> to transform the 2D coordinates it processed to coordinates on your screen. For example, a processed point of location <code>(-0.5,0.5)</code> would (as its final transformation) be mapped to <code>(200,450)</code> in screen coordinates. Note that processed coordinates in OpenGL are between -1 and 1 so we effectively map from the range (-1 to 1) to (0, 800) and (0, 600). +OpenGLは、処理した二次元の座標をスクリーンの座標に変換するために、<fun><function id='22'>glViewport</function></fun>を通してあたえられたデータを利用します。たとえばOpenGLの処理により座標が<code>(-0.5,0.5)</code>になった点は、スクリーン上の<code>(200, 450)</code>に対応します。OpenGLで処理された座標は-1と1の間におさまることに留意してください。この縦横(-1, 1)の区間はそれぞれ(0, 800), (0, 600)に対応しています。 +</note> + +<p> + However, the moment a user resizes the window the viewport should be adjusted as well. We can register a callback function on the window that gets called each time the window is resized. This resize callback function has the following prototype: +しかしユーザーがウィンドウのサイズを変更した場合、それにあわせてビューポートも変化するべきです。そのためにコールバック関数をウィンドウに登録して、ウィンドウサイズが変更されるたびに呼ばれるようにできます。次にあげるのがサイズ変更のコールバック関数のプロトタイプです: +</p> + +<pre><code> +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +</code></pre> + +<p> + The framebuffer size function takes a <fun>GLFWwindow</fun> as its first argument and two integers indicating the new window dimensions. Whenever the window changes in size, GLFW calls this function and fills in the proper arguments for you to process. +この関数は第一引数に<fun>GLFWwindow</fun>をとり、変更されたウィンドウの寸法を第二、第三引数にとります。ウィンドウサイズが変更されれば、GLFWがこの関数を呼び出し、処理に必要な変数を渡します。 +</p> + +<pre><code> +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + <function id='22'>glViewport</function>(0, 0, width, height); +} +</code></pre> + +<p> + We do have to tell GLFW we want to call this function on every window resize by registering it: +ウィンドウサイズが変更されるたびにこの関数が呼び出されるよう、以下の通り登録します: +</p> + +<pre><code> +glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); +</code></pre> + +<p> + When the window is first displayed <fun>framebuffer_size_callback</fun> gets called as well with the resulting window dimensions. For retina displays <var>width</var> and <var>height</var> will end up significantly higher than the original input values. +ウィンドウが初めて表示されたときも、<fun>framebuffer_size_callback</fun>は呼ばれ、実際に表示されたウィンドウの寸法が渡されます。レティナディスプレイでは<var>width</var>や<var>height</var>はもともと入力していた値よりもかなり大きくなります。 +</p> + +<p> + There are many callbacks functions we can set to register our own functions. For example, we can make a callback function to process joystick input changes, process error messages etc. We register the callback functions after we've created the window and before the render loop is initiated. +このほかにもコールバック関数として登録できるものはたくさんあります。ジョイスティックの入力値を処理する関数や、エラーメッセージを処理する関数等です。コールバック関数はウィンドウを作成した後、描画ループが始まる前に登録します。 +</p> + +<h1>Ready your engines</h1> +<h1>エンジンの準備</h1> +<p> + We don't want the application to draw a single image and then immediately quit and close the window. We want the application to keep drawing images and handling user input until the program has been explicitly told to stop. For this reason we have to create a while loop, that we now call the <def>render loop</def>, that keeps on running until we tell GLFW to stop. The following code shows a very simple render loop: +アプリケーションがある画像を表示したあとすぐに終了してウィンドウを閉じてしまってはおもしろくありません。プログラムが終了するまで映像を出力し続け、ユーザーからの入力を処理してほしいものです。そのために<def>描画ループ</def>と呼ばれるwhileループを作成し、ユーザーがGLFWに終了を命じるまで動き続けるようにします。以下のコードは単純な描画ループの例です: +</p> + +<pre><code> +while(!<function id='14'>glfwWindowShouldClose</function>(window)) +{ + <function id='24'>glfwSwapBuffers</function>(window); + <function id='23'>glfwPollEvents</function>(); +} +</code></pre> + +<p> + The <fun><function id='14'>glfwWindowShouldClose</function></fun> function checks at the start of each loop iteration if GLFW has been instructed to close. If so, the function returns <code>true</code> and the render loop stops running, after which we can close the application.<br/> + The <fun><function id='23'>glfwPollEvents</function></fun> function checks if any events are triggered (like keyboard input or mouse movement events), updates the window state, and calls the corresponding functions (which we can register via callback methods). + The <fun><function id='24'>glfwSwapBuffers</function></fun> will swap the color buffer (a large 2D buffer that contains color values for each pixel in GLFW's window) that is used to render to during this render iteration and show it as output to the screen. +<fun><function id='14'>glfwWindowShouldClose</function></fun>はループごとに、GLFWが終了の信号を受けとっていないか確認します。終了の信号を受けとっていればこの関数は<code>true</code>を返し描画ループが終了し、アプリケーションを終了させることができます。<fun><function id='23'>glfwPollEvents</function></fun>はキーボードからの入力やマウスの移動といったイベントが発生していないか確認し、ウィンドウの状態を更新し、コールバックとして登録した関数を呼び出します。<fun><function id='24'>glfwSwapBuffers</function></fun>はカラーバッファを入れ替えます。カラーバッファというのは大きな2次元のバッファで、GLFWウィンドウの各ピクセルの色を保持しており、この描画ループの中での描画および、その結果をスクリーンに出力するのに利用します。 +</p> + +<note> + <strong>Double buffer</strong><br/> + <strong>ダブルバッファ</strong><br/> + When an application draws in a single buffer the resulting image may display flickering issues. This is because the resulting output image is not drawn in an instant, but drawn pixel by pixel and usually from left to right and top to bottom. Because this image is not displayed at an instant to the user while still being rendered to, the result may contain artifacts. To circumvent these issues, windowing applications apply a double buffer for rendering. The <strong>front</strong> buffer contains the final output image that is shown at the screen, while all the rendering commands draw to the <strong>back</strong> buffer. As soon as all the rendering commands are finished we <strong>swap</strong> the back buffer to the front buffer so the image can be displayed without still being rendered to, removing all the aforementioned artifacts. +アプリケーションがバッファをひとつだけ使って画像を表示すると、画面にチラつきが発生します。出力された画像がすぐに表示されるのではなく、一般的には左から右へ、上から下へと1ピクセルずつ表示されるためです。描画処理が完了していない画像がじわじわと表示されるので、不自然な結果になるのです。この問題を回避するためにアプリケーションの描画にダブルバッファを利用します。<strong>フロント</strong>バッファが最終的にスクリーンに表示される出力を保持し、描画処理は<strong>バック</strong>バッファにおいて行われます。描画処理がすべて終了すれば前後のバッファが<strong>交換</strong>されます。こうすることで描画処理が完了していない状態の画像を表示させないようにでき、前述の不具合が解消できます。 +</note> + +<h2>One last thing</h2> +<h2>最後にひとつ</h2> + <p> + As soon as we exit the render loop we would like to properly clean/delete all of GLFW's resources that were allocated. We can do this via the <fun><function id='25'>glfwTerminate</function></fun> function that we call at the end of the <fun>main</fun> function. +描画ループからぬけたあと、確保したメモリを開放するのがいいでしょう。この作業は<fun><function id='25'>glfwTerminate</function></fun>によって<fun>main</fun>関数の最後に行うことができます。 + </p> + +<pre><code> +<function id='25'>glfwTerminate</function>(); +return 0; +</code></pre> + + <p> + This will clean up all the resources and properly exit the application. Now try to compile your application and if everything went well you should see the following output: +この関数により、メモリをすべて開放しアプリケーションを適切に終了させることができます。それではいちどアプリケーションをコンパイルしてみましょう。すべてうまくいっていれば以下のような出力が得られるはずです: + </p> + + <img src="/img/getting-started/hellowindow.png" width="600px" class="clean" alt="Image of GLFW window output as most basic example"/> + +<p> + If it's a very dull and boring black image, you did things right! If you didn't get the right image or you're confused as to how everything fits together, check the full source code <a href="/code_viewer_gh.php?code=src/1.getting_started/1.1.hello_window/hello_window.cpp" target="_blank">here</a> (and if it started flashing different colors, keep reading). +真っ黒でつまらない画像が表示された場合ここまでの作業がうまくいっているということです。黒い画面が表示されない場合や、各コードをどのように配置すればいいのかわからない場合は、<a href="/code_viewer_gh.php?code=src/1.getting_started/1.1.hello_window/hello_window.cpp" target="_blank">ここ</a>から完全なソースコードを確認してください(もし違う色の画像が表示された場合、とりあえずこの先を読み進んでください)。 + </p> + + <p> + If you have issues compiling the application, first make sure all your linker options are set correctly and that you properly included the right directories in your IDE (as explained in the previous chapter). Also make sure your code is correct; you can verify it by comparing it with the full source code. +コンパイルが通らなければ、まずは前章で説明した通りリンカのオプションがすべて正しく設定されているか、正しいインクルードディレクトリがIDEに読み込まれているか確認してください。そしてソースコードが正しく記述されているか確かめてください。上にあげた完全なソースコードとあなたのものを比較することで間違いがないか確認できます。 + </p> + +<h2>Input</h2> +<h2>入力</h2> + <p> + We also want to have some form of input control in GLFW and we can achieve this with several of GLFW's input functions. We'll be using GLFW's <fun>glfwGetKey</fun> function that takes the window as input together with a key. The function returns whether this key is currently being pressed. We're creating a <fun>processInput</fun> function to keep all input code organized: +GLFWにおいて入力の処理は、GLFWのいくつかの関数によって行うことができます。ここでは<fun>glfwGetKey</fun>を利用します。この関数はウィンドウと入力されたキーを引数にとり、そのキーが押されているかどうかを返します。<fun>processInput</fun>という関数を作成し、入力の処理を一元化しましょう: +</p> + +<pre><code> +void processInput(GLFWwindow *window) +{ + if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); +} +</code></pre> + +<p> + Here we check whether the user has pressed the escape key (if it's not pressed, <fun>glfwGetKey</fun> returns <var>GLFW_RELEASE</var>). If the user did press the escape key, we close GLFW by setting its <var>WindowShouldClose</var> property to <code>true</code> using <fun>glfwSetwindowShouldClose</fun>. The next condition check of the main <code>while</code> loop will then fail and the application closes. +ここではユーザーがエスケープキーを押したかどうかを確認しています(押されていなければ<fun>glfwGetKey</fun>は<var>GLFW_RELEASE</var>を返します)。エスケープが押されていた場合、<fun>glfwSetwindowShouldClose</fun>を通して<var>WindowShouldClose</var>を<code>true</code>にすることで、GLFWを終了させるようにします。こうすることで、<code>while</code>が次のループに進むかどうかの判定で偽が返り、ループから抜けだしアプリケーションが終了します。 +</p> + +<p> + We then call <fun>processInput</fun> every iteration of the render loop: +それでは<fun>processInput</fun>を描画ループがまわるたびに呼び出すようにしましょう: +</p> + +<pre><code> +while (!<function id='14'>glfwWindowShouldClose</function>(window)) +{ + processInput(window); + + <function id='24'>glfwSwapBuffers</function>(window); + <function id='23'>glfwPollEvents</function>(); +} +</code></pre> + +<p> + This gives us an easy way to check for specific key presses and react accordingly every <def>frame</def>. An iteration of the render loop is more commonly called a <def>frame</def>. +これが、<def>フレーム</def>ごとになんらかのキーが入力されたかどうかを確認する簡単な方法です。描画ループの一回は一般に<def>フレーム</def>と呼ばれます。 +</p> + +<h2>Rendering</h2> +<h2>描画</h2> +<p> + We want to place all the rendering commands in the render loop, since we want to execute all the rendering commands each iteration or frame of the loop. This would look a bit like this: +各フレームごとに描画処理を行いたいので、描画命令はすべて描画ループの中におきます。以下のような感じです: + </p> + +<pre><code> +// 描画ループ +while(!<function id='14'>glfwWindowShouldClose</function>(window)) +{ + // 入力 + processInput(window); + + // 描画処理 + ... + + // check and call events and swap the buffers + // イベントの処理およびバッファの交換 + <function id='23'>glfwPollEvents</function>(); + <function id='24'>glfwSwapBuffers</function>(window); +} +</code></pre> + + <p> + Just to test if things actually work we want to clear the screen with a color of our choice. At the start of frame we want to clear the screen. Otherwise we would still see the results from the previous frame (this could be the effect you're looking for, but usually you don't). We can clear the screen's color buffer using <fun><function id='10'>glClear</function></fun> where we pass in buffer bits to specify which buffer we would like to clear. The possible bits we can set are <var>GL_COLOR_BUFFER_BIT</var>, <var>GL_DEPTH_BUFFER_BIT</var> and <var>GL_STENCIL_BUFFER_BIT</var>. Right now we only care about the color values so we only clear the color buffer. +ほんとうにうまくいっているのか確認するために、スクリーンの色を変更してみましょう。各フレームのはじめにスクリーンに表示されたものをすべて消します。そうしないと前のフレームで表示されていたものが次のフレームでもみえたままになります(ときにはこのわざとそうしたいこともありますが、多くの場合これは望まない結果でしょう)。スクリーンのカラーバッファは<fun><function id='10'>glClear</function></fun>を用いて削除します。バッファビットを通して消したいバッファを指定しましょう。選べるバッファビットは<var>GL_COLOR_BUFFER_BIT</var>、<var>GL_DEPTH_BUFFER_BIT</var>および<var>GL_STENCIL_BUFFER_BIT</var>です。 + </p> + +<pre><code> +<function id='13'><function id='10'>glClear</function>Color</function>(0.2f, 0.3f, 0.3f, 1.0f); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT); +</code></pre> + + <p> + Note that we also specify the color to clear the screen with using <fun><function id='13'><function id='10'>glClear</function>Color</function></fun>. Whenever we call <fun><function id='10'>glClear</function></fun> and clear the color buffer, the entire color buffer will be filled with the color as configured by <fun><function id='13'><function id='10'>glClear</function>Color</function></fun>. This will result in a dark green-blueish color. +<fun><function id='13'><function id='10'>glClear</function>Color</function></fun>により、バッファを削除したあとの色も指定していることに注意してください。<fun><function id='10'>glClear</function></fun>を呼んでバッファを削除したときは<fun><function id='13'><function id='10'>glClear</function>Color</function></fun>により指定された色でバッファが満たされます。今回は深い青緑を指定しています。 +</p> + +<note> + As you may recall from the <em>OpenGL</em> chapter, the <fun><function id='13'><function id='10'>glClear</function>Color</function></fun> function is a <em>state-setting</em> function and <fun><function id='10'>glClear</function></fun> is a <em>state-using</em> function in that it uses the current state to retrieve the clearing color from. +<em>OpenGL</em>の章で状態遷移関数と状態利用関数について言及しました。ここにでてきた<fun><function id='13'><function id='10'>glClear</function>Color</function></fun>は<em>状態遷移</em>関数で、<fun><function id='10'>glClear</function></fun>は<em>状態利用</em>関数です。<fun><function id='10'>glClear</function></fun>が「バッファ削除後の色」という状態を利用しています。 +</note> + + <img src="/img/getting-started/hellowindow2.png" width="600px" class="clean" alt="Image of GLFW's window creation with <function id='13'><function id='10'>glClear</function>Color</function> defined"/> + + <p> + The full source code of the application can be found <a href="/code_viewer_gh.php?code=src/1.getting_started/1.2.hello_window_clear/hello_window_clear.cpp" target="_blank">here</a>. +今回作ったアプリケーションの完全なソースコードは<a href="/code_viewer_gh.php?code=src/1.getting_started/1.2.hello_window_clear/hello_window_clear.cpp" target="_blank">こちら</a>にあります。 + </p> + +<p> + So right now we got everything ready to fill the render loop with lots of rendering calls, but that's for the <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">next</a> chapter. I think we've been rambling long enough here. +描画ループにおいて様々なものを描くうえで必要な準備はすべて整いました。 <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">次</a>の章では実際に描画しましょう。 +</p> + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/OpenGL.html b/pub/Getting-started/OpenGL.html @@ -0,0 +1,474 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">OpenGL</h1> +<h1 id="content-url" style='display:none;'>Getting-started/OpenGL</h1> +<p>学習をはじめる前にOpenGLとは何かを確認しておきましょう。OpenGLとはグラフィックや画像を操作するための関数を多数提供するAPI(<def>アプリケーションプログラミングインターフェース</def>)だと考えられています。しかしOpenGLそのものはAPIではなく、<a href="http://www.khronos.org/" target="_blank">Khronos Group</a>により開発及びメンテナンスされている仕様です。</p> + +<img src="/img/getting-started/opengl.jpg" class="right" alt="Image of OpenGL's logo"/> + +<p> +OpenGLの仕様は各関数がどのようにふるまい、どのような結果を残し、あるいどんな出力を返すのかを正確に規定しています。これらの関数をどのようにして仕様どおりに機能させるかは、開発者の<em>実装</em>に任されています。OpenGLの仕様は実装についての詳細を規定していないので、得られる結果が仕様通りである(つまりユーザーから見て同じである)限り、様々な実装が可能です。 +</p> + +<p> +OpenGLのライブラリを開発しているのは、おもにグラフィックカードの製造者です。あなたが購入したグラフィックカードはその製品(あるいはそのシリーズ)に特化したOpenGLのバージョンをサポートしています。AppleのシステムではOpenGLのライブラリはApple自身によりメンテナンスされており、Linuxにおいてはグラフィックカードの製造者によるライブラリや、趣味の開発者によるものもあります。つまりOpenGLがおかしな挙動をする場合はたいていグラフィックカードの製造者(またはライブラリの開発に関わった誰か)のミスです。 +</p> + +<note> +ほとんどの実装はグラフィックカードの製造者によるものなので、バグが見付かった場合はビデオカードのドライバをアップデートすることでたいてい解決します。最新のドライバにはそのグラフィックカードがサポートする最新のOpenGLが含まれているからです。グラフィックドライバをときどきアップデートするべき理由のひとつです。 +</note> + +<p> +KhronosはすべてのOpenGLのバージョンの仕様に関するドキュメントを公開しています。興味がある読者はこれからみなさんが利用するバージョン3.3の仕様を<a href="https://www.opengl.org/registry/doc/glspec33.core.20100311.withchanges.pdf" target="_blank">ここ</a>で確認できます。このページはOpenGLの詳細を理解するのに非常に役立ちます(実装ではなく結果ばかりが記述されていることを確認してください)。OpenGLの仕様は関数の<strong>正確な</strong>ふるまいを確認する文献としても重要です。 +</p> + + +<h2>Core-profileと即席モード</h2> +<p> +もともと、OpenGLを利用するとは<def>即席モード</def>(<def>固定関数パイプライン</def>とも)で開発するということでした。即席モードはグラフィックを描写する簡易な方法です。OpenGLのほとんどの機能はライブラリのなかに隠され、開発者は演算の方法にまで深くは踏み込めませんでした。開発者はしだいに柔軟性を求めるようになり、それに伴い仕様のほうも柔軟になりました。開発者がグラフィックの描画方法にに関してより多くのことを操作できるようになったのです。即席モードは理解し使用するのは非常に簡単ですが、効率はかなり悪いです。そのため仕様はバージョン3.2から即席モードの廃止に向かい、<def>core-profile</def>モードを推奨しはじめました。core-profileモードでは廃止された古い機能がすべて取り除かれています。 +</p> + +<p> +Core-profileを使うには新しい方法に従う必要があります。廃止された関数を使おうとするとOpenGLがエラーを出して描画を停止します。新しい方法の利点は柔軟性の高さと効率のよさです。ただしその分習得するのが難しいです。即席モードはOpenGLがおこなっている<strong>実際の</strong>操作の多くを抽象化しているので習得するのが簡単な一方、OpenGLが実際になにをおこなっているかを把握するのは難しいです。新しい方法ではOpenGLやグラフィックスプログラミングについての深い理解が必要であるのですこし難解ではあるものの、柔軟性や効率のよさ、そして一番重要なことですが、グラフィックスプログラミングに関するよりよい理解が得られます。 +</p> + +<p> +そのためこの本ではOpenGLバージョン3.3のcore-profileについて重点的に解説します。理解するのは大変ですが、努力する価値は十分にあります。 +</p> + +<p> +今日、OpenGLのもっと新しいバージョンが利用できます(執筆時点での最新は4.6)。にも関わらずバージョン3.3を学習する意味があるのかと疑問に思うかもしれませんが、その答えは比較的簡単です。バージョン3.3より新しいものはすべて、このバージョンに便利な機能を追加したものであり、OpenGLの核となる部分は変更されません。新しいバージョンのものは以前と同じことをするうえでより効率がいい、あるいはより使いやすい方法を導入しただけです。考え方ややり方はバージョン3.3以降変わらないので、このバージョンを学ぶことが有効なのです。バージョン3.3でOpenGLに慣れ親しめば、より新しいバージョンの機能を利用することは簡単です。 +</p> + +<warning> +最新のOpenGLを使って開発したアプリケーションは、最新のグラフィックカード以外では動作しません。そのためほとんどの開発者は古いバージョンのOpenGLで開発し、新しいバージョンの機能は対応するグラフィックカードへのオプションとして組込みます。 +</warning> + +<p> +一部の章ではことわったうえで新しい機能を利用することがあります。 +</p> + +<h2>拡張機能</h2> +<p> +OpenGLの大きな特徴に、拡張機能があります。グラフィックカードの製造者が新しい技術を導入したり、描画を大幅に最適化した場合、それらの機能はドライバに実装される<def>拡張機能</def>によって提供されることが多いです。アプリケーションが動作しているハードウェアがそういった拡張機能をサポートしていれば、開発者はそれらを利用し先進的な、あるいは効率のいい方法でグラフィックを描画することができます。このような最新の機能は、グラフィックカードによりサポートされているかどうかを確認するだけで利用できるので、開発者はOpenGLにその機能が組込まれるのを待つ必要がありません。ある拡張機能が人気になり、あるいは便利であれば将来のOpenGLのバージョンに組込まれることになります。 +</p> + +<p> +上記のようなグラフィックカードに特有の(あるいはOpenGL自身の)拡張機能を利用する前に、その機能が利用可能かどうかを確かめる必要があります。こうすることで、拡張機能が利用可能かどうかに応じてより効率のよいプログラムを書くことができます: +</p> + +<pre><code> +if(GL_ARB_extension_name) +{ + // 最新の機能を利用したクールなコード +} +else +{ + // 拡張機能がサポートされていない場合: 従来の方法を利用したコード +} +</code></pre> +<p> +OpenGLバージョン3.3において拡張機能はほとんど必要ありませんが、利用する場合は解説を付けます。 +</p> + +<h2>状態機械</h2> +<p> +OpenGLはひとつの大きな状態機械だといえます: 各時点におけるOpenGLの動作を規定する変数の集まりだという意味です。OpenGLの状態は<def>コンテクスト</def>と呼ばれます。OpenGLでは、オプションの設定やバッファの操作により状態を変化させ、その時点のコンテクストを利用して描画をおこないます。 +</p> + +<p> +例えば描画するものを三角形から直線に変更するためには、コンテクストの変数のうち描画する図形を規定するものを変更することでOpenGLの状態を変化させることになります。そのようにコンテクストを変更すれば、以降の描画命令では三角形ではなく直線が描かれます。 +</p> + +<p> +OpenGLでの開発において、コンテクストを変更する<def>状態変更</def>関数や、現在の状態にもとづいてなんらかの操作をおこなう<def>状態利用</def>関数を利用することになります。OpenGLが大きな状態機械であるということを頭にいれておけば、さまざまな機能を理解するのが楽になります。 +</p> + +<h2>オブジェクト</h2> +<p> +OpenGLのライブラリはC言語で記述されています。ほかの言語への派生も存在しますが、核となる部分はC言語のライブラリのままです。C言語の言語構造は他の高水準の言語にうまく翻訳できないので、OpenGLはいくつかの抽象的な概念を念頭に開発されました。<def>オブジェクト</def>の概念がそのひとつです。 +</p> + +<p> +OpenGLにおいて<def>オブジェクト</def>とは、OpenGLの状態をあらわしたオプションのあつまりです。たとえばウィンドウの描画に係る設定をまとめたオブジェクトを作ることができます。このオブジェクトを通して、ウィンドウの大きさや表示できる色の数等を設定することができます。オブジェクトはC言語の構造体のようなものとしてとらえることができます: +</p> + +<pre><code> +struct object_name { + float option1; + int option2; + char[] name; +}; +</code></pre> + +<p> +オブジェクトはたいてい以下のようなかたちで利用することになります(前半の部分はOpenGLのコンテクストを大きな構造体として記述したものです): +</p> + +<pre><code> +// OpenGLの状態 +struct OpenGL_Context { + ... + object_name* object_Window_Target; + ... +}; +</code></pre> + +<pre><code> +// オブジェクトの作成 +unsigned int objectId = 0; +glGenObject(1, &amp;objectId); +// コンテクストに対してオブジェクトを紐付け +glBindObject(GL_WINDOW_TARGET, objectId); +// 現在GL_WINDOW_TARGETに紐付いているオブジェクトのオプションを設定 +glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800); +glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600); +// コンテクストの紐付けを解除 +glBindObject(GL_WINDOW_TARGET, 0);。 +</code></pre> + +<p> +上のような記述はOpenGLで開発をしているとよくみかけます。まずオブジェクトを作成しそれを参照するためのIDを記憶しておきます(オブジェクトの実際のデータは開発者からは見えません)。つぎに記憶したIDによりオブジェクトをコンテクストのうち設定したいものに紐付けます(上の例では<var>GL_WINDOW_TARGET</var>に紐付けています)。そしてウィンドウのオプションを設定し、最後に<var>GL_WINDOW_TARGET</var>と紐付いたオブジェクトのIDを<code>0</code>にすることで、オブジェクトとコンテクストの紐付けを解除します。設定したオプションは<var>objectID</var>によって参照されるオブジェクトに保持され、オブジェクトを<var>GL_WINDOW_TARGET</var>と紐付けることでいつでも復元できます。 +</p> + +<warning> +ここまでのサンプルコードはOpenGLの操作をおおまかに記述したものです。以降では実際に動作するサンプルコードを多く提供します。 +</warning> + +<p> +オブジェクトを利用する大きな利点は、複数のオブジェクトを定義、設定しておけば、OpenGLの状態を利用してなにかするときにそれらのオブジェクトのなかから用途にあわせたものを選べることです。例えば家やキャラクター等の3Dモデルのデータを保持した複数のオブジェクトを定義しておけば、各オブジェクトを紐付けるだけで、そのオブジェクトが保持している3Dモデルが描画できます(最初に各3Dモデルに対してオブジェクトを作成し、必要なオプションを設定すればいいのです)。こうすることでたくさんのモデルを描画するときにいちいちオプションを設定しなおさなくてすみます。 +</p> + +<h2>さあ、始めましょう</h2> +<p> +ここまでOpenGLについてざっくりと学びました。OpenGLが仕様であり、ライブラリであること。OpenGLの内部がどのように機能するのか、またOpenGLをどのように扱うのか。すべてを理解できていなくても心配はいりません。この本では各段階においてOpenGLを理解するのに十分な例が提示されます。 +</p> + +<h2>参考</h2> +<ul> + <li><a href="https://www.opengl.org/" target="_blank">opengl.org</a>: OpenGLの公式ウェブサイト。</li> + <li><a href="https://www.opengl.org/registry/" target="_blank">OpenGL registry</a>: OpenGLのすべてのバージョンの仕様と拡張機能が確認できるサイト。</li> +</ul> + + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Review.html b/pub/Getting-started/Review.html @@ -0,0 +1,397 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Review</h1> + <h1 id="content-title">まとめ</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Review</h1> +<p> + Congratulations on reaching the end of the <em>Getting started</em> chapters. By now you should be able to create a window, create and compile shaders, send vertex data to your shaders via buffer objects or uniforms, draw objects, use textures, understand vectors and matrices and combine all that knowledge to create a full 3D scene with a camera to play around with. +おめでとうございます。これにて<em>入門</em>の節は完了です。ここまで読んだ方はウィンドウを作成し、シェーダーを作成及びコンパイルし、バッファオブジェクトやユニフォームを通して頂点のデータをシェーダーに送信し、物体を描画し、テクスチャを利用し、ベクトルや行列を理解しそれらを組み合わせて3次元の世界をカメラと共に作成することができるようになりました。 +</p> + +<p> + Phew, there is a lot that we learned these last few chapters. Try to play around with what you learned, experiment a bit or come up with your own ideas and solutions to some of the problems. As soon as you feel you got the hang of all the materials we've discussed it's time to move on to the <a href="https://learnopengl.com/Lighting/Colors" target="_blank">next</a> Lighting chapters. + ここまで多くのことを学びました。学んだことを確認、実験し、あるいは各自のアイデアを試し、ぶつかった問題を解決して下さい。この節で学んだことを自分の物にできたら、<a href="https://learnopengl.com/Lighting/Colors" target="_blank">次</a>の照明の節に進みましょう。 +</p> + +<h2>Glossary</h2> +<h2>用語集</h2> +<p> + <ul> + <li><code>OpenGL</code>: a formal specification of a graphics API that defines the layout and output of each function. </li> + <li><code>OpenGL</code>: グラフィックAPIの正式な仕様。各関数の入出力を定義したもの。</li> + <li><code>GLAD</code>: an extension loading library that loads and sets all OpenGL's function pointers for us so we can use all (modern) OpenGL's functions. </li> + <li><code>GLAD</code>: 拡張機能を読み込むライブラリ。OpenGLの関数のポイタを設定し、それらを利用可能な状態にするもの。</li> + <li><code>Viewport</code>: the 2D window region where we render to. </li> + <li><code>可視領域(Viewport)</code>: 映像を描画するウィンドウの2次元領域。</li> + <li><code>Graphics Pipeline</code>: the entire process vertices have to walk through before ending up as one or more pixels on the screen. </li> + <li><code>グラフィックパイプライン(Graphics Pipeline)</code>: 頂点データが画面上のピクセルに変換されるまでに行われる処理。</li> + <li><code>Shader</code>: a small program that runs on the graphics card. Several stages of the graphics pipeline can use user-made shaders to replace existing functionality.</li> + <li><code>シェーダ(Shader)</code>: グラフィックカード上で実行される小さなプログラム。グラフィックパイプライン上のいくつかの処理はユーザーが独自に作成したシェーダにより置き換えることが出来る。</li> + <li><code>Vertex</code>: a collection of data that represent a single point. </li> + <li><code>頂点(Vertex)</code>: 点を表わすデータの集合。</li> + <li><code>Normalized Device Coordinates</code>: the coordinate system your vertices end up in after perspective division is performed on clip coordinates. All vertex positions in NDC between <code>-1.0</code> and <code>1.0</code> will not be discarded or clipped and end up visible. </li> + <li><code>正規化デバイス座標(Normalized Device Coordinates)</code>: クリップ座標における透視除算の後に頂点が納まるべき座標系。各要素が<code>-1.0</code>と<code>1.0</code>の間にあれば切り落されずに残り、画面上に表示される。</li> + <li><code>Vertex Buffer Object</code>: a buffer object that allocates memory on the GPU and stores all the vertex data there for the graphics card to use. </li> + <li><code>頂点バッファオブジェクト(Vertex Buffer Object)</code>: GPU上のメモリを確保しそこに頂点データを保存するバッファオブジェクト。</li> + <li><code>Vertex Array Object</code>: stores buffer and vertex attribute state information.</li> + <li><code>頂点配列オブジェクト(Vertex Array Object)</code>: バッファと頂点属性の状態を保存する。</li> + <li><code>Element Buffer Object</code>: a buffer object that stores indices on the GPU for indexed drawing. </li> + <li><code>要素バッファオブジェクト(Element Buffer Object)</code>: インデックスによる描画に利用するインデックスをGPUに保存するバッファオブジェクト。</li> + <li><code>Uniform</code>: a special type of GLSL variable that is global (each shader in a shader program can access this uniform variable) and only has to be set once. </li> + <li><code>ユニフォーム(Uniform)</code>: GLSLの特別な変数。大域的(シェーダープログラム中の各シェーダーからこの変数にアクセスできる)で一度値が決まれば変更できない。</li> + <li><code>Texture</code>: a special type of image used in shaders and usually wrapped around objects, giving the illusion an object is extremely detailed. </li> + <li><code>テクスチャ(Texture)</code>: シェーダーで利用される画像。通常物体に貼り付けられその物体を微細に描くために用いられる。</li> + <li><code>Texture Wrapping</code>: defines the mode that specifies how OpenGL should sample textures when texture coordinates are outside the range: (<code>0</code>, <code>1</code>). </li> + <li><code>テクスチャの繰り返し(Texture Wrapping)</code>: <code>0</code>と<code>1</code>の外のテクスチャ座標においてOpenGLがどのようにテクスチャをサンプリングするかを定める。</li> + <li><code>Texture Filtering</code>: defines the mode that specifies how OpenGL should sample the texture when there are several texels (texture pixels) to choose from. This usually occurs when a texture is magnified. </li> + <li><code>テクスチャフィルタリング(Texture Filtering)</code>: テクスチャのピクセルであるテクセルが複数ある場合にどのようにサンプリングするかを定める。テクスチャが拡大される時に利用される。</li> + <li><code>Mipmaps</code>: stored smaller versions of a texture where the appropriate sized version is chosen based on the distance to the viewer. </li> + <li><code>ミップマップ(Mipmaps)</code>: テクスチャを縮小したものを保存。カメラからの距離に応じて適切なサイズのテクスチャを選ぶために利用。</li> + <li><code>stb_image</code>: image loading library. </li> + <li><code>stb_image</code>: 画像を読み込むためのライブラリ。</li> + <li><code>Texture Units)</code>: allows for multiple textures on a single shader program by binding multiple textures, each to a different texture unit. </li> + <li><code>テクスチャユニット(Texture Units)</code>: 単一のシェーダープログラムに対して複数のテクスチャを紐付けるためのもの。それぞれのテクスチャをそれぞれのテクスチャユニットに割り当てる。</li> + <li><code>Vector</code>: a mathematical entity that defines directions and/or positions in any dimension. </li> + <li><code>ベクトル(Vector)</code>: 任意の次元の空間において方向や位置を定める数学の道具。</li> + <li><code>Matrix</code>: a rectangular array of mathematical expressions with useful transformation properties. </li> + <li><code>行列(Matrix)</code>: 数式を四角に並べたもの。座標変換において便利。</li> + <li><code>GLM</code>: a mathematics library tailored for OpenGL. </li> + <li><code>GLM</code>: OpenGLに特化した数学ライブラリ。</li> + <li><code>Local Space</code>: the space an object begins in. All coordinates relative to an object's origin. </li> + <li><code>局所空間(Local Space)</code>: 各物体に固有の座標空間。</li> + <li><code>World Space</code>: all coordinates relative to a global origin. </li> + <li><code>大域空間(World Space)</code>: 大域的な原点に相対的な座標。</li> + <li><code>View Space</code>: all coordinates as viewed from a camera's perspective. </li> + <li><code>視野空間(View Space)</code>: カメラから見た座標。</li> + <li><code>Clip Space</code>: all coordinates as viewed from the camera's perspective but with projection applied. This is the space the vertex coordinates should end up in, as output of the vertex shader. OpenGL does the rest (clipping/perspective division). </li> + <li><code>クリップ空間(Clip Space)</code>: カメラから見た座標を投影したもの。頂点座標は最終的にこの座標に変換されシェーダーからの出力となる。この後OpenGLがクリッピングと透視除算を行う。</li> + <li><code>Screen Space</code>: all coordinates as viewed from the screen. Coordinates range from <code>0</code> to screen width/height. </li> + <li><code>スクリーン空間(Screen Space)</code>: スクリーンから見た座標。座標は<code>0</code>とスクリーンの幅、高さの間。</li> + <li><code>LookAt</code>: a special type of view matrix that creates a coordinate system where all coordinates are rotated and translated in such a way that the user is looking at a given target from a given position. </li> + <li><code>視点行列(LookAt)</code>: 視野行列のひとつ。与えられた場所から与えられた位置を見つめるように座標変換するもの。</li> + <li><code>Euler Angles</code>: defined as <code>yaw</code>, <code>pitch</code> and <code>roll</code> that allow us to form any 3D direction vector from these 3 values. </li> + <li><code>オイラー角(Euler Angles)</code>: <code>yaw</code>(方位角)、<code>pitch</code>(仰角)、<code>roll</code>(傾斜角)によって3次元空間のあらゆる方向を定める。</li> + </ul> +</p> + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Shaders.html b/pub/Getting-started/Shaders.html @@ -0,0 +1,1014 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Shaders</h1> + <h1 id="content-title">シェーダー</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Shaders</h1> +<p> + As mentioned in the <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">Hello Triangle</a> chapter, shaders are little programs that rest on the GPU. These programs are run for each specific section of the graphics pipeline. In a basic sense, shaders are nothing more than programs transforming inputs to outputs. Shaders are also very isolated programs in that they're not allowed to communicate with each other; the only communication they have is via their inputs and outputs. +<a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">はじめての三角形</a>の章で説明したように、シェーダーはGPUで実行する小さなプログラムです。このプログラムはグラフィックスパイプラインの特定の場所で実行されます。基本的にシェーダーというのは入力をなんらかの形で変換して出力するものです。シェーダーは入出力以外の手段で互いに通信できないので、非常に独立性の高いプログラムだといえます。 +</p> + +<p> + In the previous chapter we briefly touched the surface of shaders and how to properly use them. We will now explain shaders, and specifically the OpenGL Shading Language, in a more general fashion. +前章においてシェーダーの表面的な部分と適切な使い方について少し触れました。ここではシェーダーについて、特にOpenGLのシェーディング言語について、もう少し全般的に見ていきましょう。 +</p> + +<h1>GLSL</h1> +<p> + Shaders are written in the C-like language GLSL. GLSL is tailored for use with graphics and contains useful features specifically targeted at vector and matrix manipulation. +シェーダーはC言語に似たGLSLで記述されます。GLSLはグラフィックを扱うために作られ、便利な機能、特にベクトルと行列に関するものが含まれます。 +</p> + +<p> + Shaders always begin with a version declaration, followed by a list of input and output variables, uniforms and its <fun>main</fun> function. Each shader's entry point is at its <fun>main</fun> function where we process any input variables and output the results in its output variables. Don't worry if you don't know what uniforms are, we'll get to those shortly. +シェーダーはバージョンの宣言から始まり、入力変数と出力変数およびユニフォームの列挙、そして<fun>main</fum>関数が続きます。シェーダーは<fun>main</fun>関数から実行が開始され、その関数内で入力変数を処理しその結果を出力変数に格納して返します。ユニフォームについてはすぐ後で説明しますので、今は気にしないで下さい。 +</p> + +<p> + A shader typically has the following structure: +シェーダーは典型的には以下のような構造をしています: +</p> + +<pre><code> +#version version_number +in type in_variable_name; +in type in_variable_name; + +out type out_variable_name; + +uniform type uniform_name; + +void main() +{ + // process input(s) and do some weird graphics stuff + // 入力の処理とグラフィックに関するいろいろ + ... + // output processed stuff to output variable + // 出力変数に処理したものを出力 + out_variable_name = weird_stuff_we_processed; +} +</code></pre> + +<p> + When we're talking specifically about the vertex shader each input variable is also known as a <def>vertex attribute</def>. There is a maximum number of vertex attributes we're allowed to declare limited by the hardware. OpenGL guarantees there are always at least 16 4-component vertex attributes available, but some hardware may allow for more which you can retrieve by querying <var>GL_MAX_VERTEX_ATTRIBS</var>: +頂点シェーダーにおいて、入力変数は<def>頂点属性</def>と呼ばれます。宣言できる頂点属性の最大数はハードウェアにより決定されます。OpenGLは最低でも、4要素からなる頂点属性を16個利用できることを保証していますが、ハードウェアによってはもっと利用できます。その最大数は<var>GL_MAX_VERTEX_ATTRIBS</var>から確認できます: +</p> + +<pre><code> +int nrAttributes; +glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes); +std::cout &lt;&lt; "Maximum nr of vertex attributes supported: " &lt;&lt; nrAttributes &lt;&lt; std::endl; +</code></pre> + +<p> + This often returns the minimum of <code>16</code> which should be more than enough for most purposes. +多くの場合この数字はOpenGLが定める最低である<code>16</code>になっていますが、ほとんどの場合それで十分です。 +</p> + +<h2>Types</h2> +<h2>型</h2> +<p> + GLSL has, like any other programming language, data types for specifying what kind of variable we want to work with. GLSL has most of the default basic types we know from languages like C: <code>int</code>, <code>float</code>, <code>double</code>, <code>uint</code> and <code>bool</code>. GLSL also features two container types that we'll be using a lot, namely <code>vectors</code> and <code>matrices</code>. We'll discuss matrices in a later chapter. +他のプログラミング言語と同様に、GLSLにも変数の型があります。C言語のようなプログラミング言語において、基本的な型といえば、<code>int</code>、<code>float</code>、<code>double</code>、<code>uint</code>そして<code>bool</code>といったものですが、GLSLはこのような基本的な型に加えて<code>ベクトル</code>と<code>行列</code>という容器のような型があります。この二つの型はこれから頻繁に利用するものです。行列については別の章で説明します。 +</p> + +<h3>Vectors</h3> +<h3>ベクトル</h3> +<p> + A vector in GLSL is a 1,2,3 or 4 component container for any of the basic types just mentioned. They can take the following form (<code>n</code> represents the number of components): +GLSLにおけるベクトルは上に記した基本的な型の数値を1から4個、要素として持つ容器です。これには以下のような形式があります(<code>n</code>は要素数です): +</p> + + <ul> + <li><code>vecn</code>: the default vector of <code>n</code> floats.</li> + <li><code>bvecn</code>: a vector of <code>n</code> booleans.</li> + <li><code>ivecn</code>: a vector of <code>n</code> integers.</li> + <li><code>uvecn</code>: a vector of <code>n</code> unsigned integers.</li> + <li><code>dvecn</code>: a vector of <code>n</code> double components.</li> + </ul> + <ul> + <li><code>vecn</code>: <code>n</code>個の浮動小数点数からなるデフォルトのベクトル。</li> + <li><code>bvecn</code>: <code>n</code>個の真偽値からなるベクトル。</li> + <li><code>ivecn</code>: <code>n</code>個の整数からなるベクトル。</li> + <li><code>uvecn</code>: <code>n</code>個の符号なし整数からなるベクトル。</li> + <li><code>dvecn</code>: <code>n</code>個の倍精度浮動小数点数からなるベクトル。</li> + </ul> + +<p> + Most of the time we will be using the basic <code>vecn</code> since floats are sufficient for most of our purposes. +本書においては浮動小数点数で十分間に合うので、主に<code>vecn</code>を利用します。 +</p> + + <p> + Components of a vector can be accessed via <code>vec.x</code> where <code>x</code> is the first component of the vector. You can use <code>.x</code>, <code>.y</code>, <code>.z</code> and <code>.w</code> to access their first, second, third and fourth component respectively. GLSL also allows you to use <code>rgba</code> for colors or <code>stpq</code> for texture coordinates, accessing the same components. +ベクトルの要素には<code>vec.x</code>のような形でアクセスできます。1〜4番目の要素はそれぞれ<code>.x</code>、<code>.y</code>、<code>.z</code>、<code>.w</code>と記述します。GLSLでは他にも、ベクトルを色の表現として利用する場合には<code>rgba</code>により、またテクスチャの座標には<code>stpq</code>により、各要素にアクセスすることができます。 + </p> + + <p> + The vector datatype allows for some interesting and flexible component selection called <def>swizzling</def>. Swizzling allows us to use syntax like this: +ベクトルの要素を選択する、おもしろくて柔軟性の高い方法があります。<def>スウィズリング</def>と呼ばれるこの方法は以下のように利用します: + </p> + +<pre><code> +vec2 someVec; +vec4 differentVec = someVec.xyxx; +vec3 anotherVec = differentVec.zyw; +vec4 otherVec = someVec.xxxx + anotherVec.yxzy; +</code></pre> + +<p> + You can use any combination of up to 4 letters to create a new vector (of the same type) as long as the original vector has those components; it is not allowed to access the <code>.z</code> component of a <code>vec2</code> for example. We can also pass vectors as arguments to different vector constructor calls, reducing the number of arguments required: +あるベクトルを用いて、その要素から好きな組み合わせにより同じ型の新しいベクトルを作れるのです。ただし例えば<code>vec2</code>の<code>.z</code>要素は存在しないので利用できません。あるいは以下のように、あるベクトルを他のベクトルのコンストラクタに渡すこともできます: +</p> + +<pre><code> +vec2 vect = vec2(0.5, 0.7); +vec4 result = vec4(vect, 0.0, 0.0); +vec4 otherResult = vec4(result.xyz, 1.0); +</code></pre> + +<p> + Vectors are thus a flexible datatype that we can use for all kinds of input and output. Throughout the book you'll see plenty of examples of how we can creatively manage vectors. +このようにベクトルは柔軟性の高い型で、いつでも入力や出力として用いることができます。本書を通じて創造的にベクトルを使う例がたくさん見られます。 +</p> + +<h2>Ins and outs</h2> +<h2>入力と出力</h2> +<p> + Shaders are nice little programs on their own, but they are part of a whole and for that reason we want to have inputs and outputs on the individual shaders so that we can move stuff around. GLSL defined the <code>in</code> and <code>out</code> keywords specifically for that purpose. Each shader can specify inputs and outputs using those keywords and wherever an output variable matches with an input variable of the next shader stage they're passed along. The vertex and fragment shader differ a bit though. +シェーダーはそれだけで完結した小さなプログラムですが、それらを組み合わて全体としてうまく機能する必要があります。そのため、互いにデータをやりとりするための入力と出力がそれぞれのシェーダーで必要になります。GLSLには入出力を宣言するための<code>in</code>と<code>out</code>というキーワードが用意されています。各シェーダーではこれらのキーワードを使って入力と出力を定義し、あるシェーダーの出力が次のシェーダーの入力と合致すればそこでデータの受け渡しが行われます。ただし頂点シェーダーとフラグメントシェーダーは少し事情が異なります。 +</p> + +<p> + The vertex shader <strong>should</strong> receive some form of input otherwise it would be pretty ineffective. The vertex shader differs in its input, in that it receives its input straight from the vertex data. To define how the vertex data is organized we specify the input variables with location metadata so we can configure the vertex attributes on the CPU. We've seen this in the previous chapter as <code>layout (location = 0)</code>. The vertex shader thus requires an extra layout specification for its inputs so we can link it with the vertex data. +頂点シェーダーは特定の形式の入力を持つことが<strong>強く推奨</strong>されます。そうしないとかなり効率が落ちます。頂点シェーダーは他のシェーダーとは違い、入力として頂点データを直接受け取ります。CPU上で設定した頂点データがどのように構成されているかをシェーダーに伝えるため、入力に対してデータの位置というメタデータを記述します。前章で<code>layout (location = 0)</code>というコードを記述したのがそれです。頂点シェーダーへの入力を頂点データと紐付けるため、このようにデータの配置を特定する必要があるのです。 + </p> + +<note> + It is also possible to omit the <code>layout (location = 0)</code> specifier and query for the attribute locations in your OpenGL code via <fun><function id='104'>glGetAttribLocation</function></fun>, but I'd prefer to set them in the vertex shader. It is easier to understand and saves you (and OpenGL) some work. +<code>layout (location = 0)</code>を使わず、<fun><function id='104'>glGetAttribLocation</function></fun>によって属性の位置を特定することも可能ですが、著者は前述の方法を好みます。理解しやすく開発者(とOpenGL)の労力を節約できるからです。 + </note> + +<p> + The other exception is that the fragment shader requires a <code>vec4</code> color output variable, since the fragment shaders needs to generate a final output color. If you fail to specify an output color in your fragment shader, the color buffer output for those fragments will be undefined (which usually means OpenGL will render them either black or white). +もう一つの例外であるフラグメントシェーダーはその出力が<code>vec4</code>型の色でなければなりません。このシェーダーは最終的な色を生成する必要があるからです。フラグメントシェーダーにおいてあるフラグメントに対して色を出力できなかった場合、そのフラグメントの色バッファがどうなるかは不定です(多くの場合黒または白で描画されます)。 + </p> + +<p> + So if we want to send data from one shader to the other we'd have to declare an output in the sending shader and a similar input in the receiving shader. When the types and the names are equal on both sides OpenGL will link those variables together and then it is possible to send data between shaders (this is done when linking a program object). To show you how this works in practice we're going to alter the shaders from the previous chapter to let the vertex shader decide the color for the fragment shader. +あるシェーダーから次のシェーダーにデータを送信する場合、送信元の出力と受信先の入力を同じ形式で宣言しなければいけません。双方の型と名前が一致した場合、OpenGLがそれを接続しデータの受け渡しが可能になります(この作業はプログラムオブジェクトのリンク時に行われます)。実際の動作を確認するために、前章で作成したシェーダーを変更して、頂点シェーダーがフラグメントシェーダーの色を決定するようにしましょう。 + </p> + +<strong>Vertex shader</strong> +<strong>頂点シェーダー</strong> +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; // the position variable has attribute position 0 +layout (location = 0) in vec3 aPos; // 位置変数は位置0に存在 + +out vec4 vertexColor; // specify a color output to the fragment shader +out vec4 vertexColor; // フラグメントシェーダーへの出力として色を指定 + +void main() +{ + gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor + vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color + gl_Position = vec4(aPos, 1.0); // このようにすればvec3からvec4を直接作成できます + vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 出力する色を暗い赤に +} +</code></pre> + +<strong>Fragment shader</strong> +<strong>フラグメントシェーダー</strong> +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec4 vertexColor; // the input variable from the vertex shader (same name and same type) +in vec4 vertexColor; // 頂点シェーダーからの入力(同じ名前かつ同じ型) + +void main() +{ + FragColor = vertexColor; +} +</code></pre> + +<p> + You can see we declared a <var>vertexColor</var> variable as a <code>vec4</code> output that we set in the vertex shader and we declare a similar <var>vertexColor</var> input in the fragment shader. Since they both have the same type and name, the <var>vertexColor</var> in the fragment shader is linked to the <var>vertexColor</var> in the vertex shader. Because we set the color to a dark-red color in the vertex shader, the resulting fragments should be dark-red as well. The following image shows the output: +頂点シェーダーにおいて<code>vec4</code>の変数<var>vertexColor</var>を宣言し、フラグメントシェーダーでも同様のものを定義します。双方が同じ名前で同じ型の変数なので、フラグメントシェーダーの<var>vertexColor</var>は頂点シェーダーの<var>vertexColor</var>と接続されます。頂点シェーダーにおいて暗い赤に設定したので、フラグメントシェーダーからも暗い赤が出力されます。その結果以下のような画像が生成されます: + </p> + + <img src="/img/getting-started/shaders.png" class="clean"/> + +<p> + There we go! We just managed to send a value from the vertex shader to the fragment shader. Let's spice it up a bit and see if we can send a color from our application to the fragment shader! +いかがでしょう。頂点シェーダーからフラグメントシェーダーに変数を送信できました。これに少し味付けして、アプリケーションからフラグメントシェーダーに色の情報を送信するようにしましょう。 + </p> + + <h2>Uniforms</h2> + <h2>ユニフォーム</h2> + <p> + <def>Uniforms</def> are another way to pass data from our application on the CPU to the shaders on the GPU. Uniforms are however slightly different compared to vertex attributes. First of all, uniforms are <def>global</def>. Global, meaning that a uniform variable is unique per shader program object, and can be accessed from any shader at any stage in the shader program. Second, whatever you set the uniform value to, uniforms will keep their values until they're either reset or updated. +<def>ユニフォーム</def>はCPU上のアプリケーションからGPU上のシェーダーにデータを送信するもうひとつの方法です。ユニフォームは頂点属性と少し違います。第一にユニフォームは<def>グローバル</def>です。すなわち、ユニフォームは各シェーダープログラムオブジェクトにおいて一意であり、同シェーダープログラム中の他のシェーダーからもアクセスできます。第二に、ユニフォームの値がセットされた場合、その値はリセットあるいは更新されるまで一定です。 + </p> + + <p> + To declare a uniform in GLSL we simply add the <code>uniform</code> keyword to a shader with a type and a name. From that point on we can use the newly declared uniform in the shader. Let's see if this time we can set the color of the triangle via a uniform: +GLSLにおいてユニフォームを宣言するには<code>uniform</code>キーワードを変数の型と名前に添えてシェーダーに記述するだけです。以降そのシェーダーにおいてこのユニフォームが利用できます。それではユニフォームを使って三角形の色が変更できるか試してみましょう: + </p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +uniform vec4 ourColor; // we set this variable in the OpenGL code. +uniform vec4 ourColor; // この変数の値をOpenGLのコードで設定します。 + +void main() +{ + FragColor = ourColor; +} +</code></pre> + + <p> + We declared a uniform <code>vec4</code> <var>ourColor</var> in the fragment shader and set the fragment's output color to the content of this uniform value. Since uniforms are global variables, we can define them in any shader stage we'd like so no need to go through the vertex shader again to get something to the fragment shader. We're not using this uniform in the vertex shader so there's no need to define it there. +<code>vec4</code> <var>ourColor</var>というユニフォームをフラグメントシェーダーで宣言し、フラグメントシェーダーの出力の色をこのユニフォームの値にしました。ユニフォームはグローバル変数なので、どのシェーダーにおいて定義することも可能です。そのため頂点シェーダーにおいてフラグメントシェーダーのために何かする必要はありません。頂点シェーダーにおいてこのユニフォームを利用しないので、そこで定義する必要もありません。 + </p> + + <warning> + If you declare a uniform that isn't used anywhere in your GLSL code the compiler will silently remove the variable from the compiled version which is the cause for several frustrating errors; keep this in mind! +定義したユニフォームがどのGLSLのコードにおいて全く利用されない場合、コンパイラは黙ってその変数を削除します。この仕様は時にうっとうしいエラーの原因になります。頭の片隅に置いておいて下さい 。 + </warning> + + <p> + The uniform is currently empty; we haven't added any data to the uniform yet so let's try that. We first need to find the index/location of the uniform attribute in our shader. Once we have the index/location of the uniform, we can update its values. Instead of passing a single color to the fragment shader, let's spice things up by gradually changing color over time: +このユニフォームはまだデータを追加していないので空っぽです。ここにデータを渡す方法を見ていきましょう。まずシェーダーにおけるユニフォームの場所を探す必要があります。ユニフォームが見つかったらその値を更新します。一色だけ指定するのでは面白くないので、時間と共に色を変化させてみましょう。 + </p> + +<pre><code> +float timeValue = <function id='47'>glfwGetTime</function>(); +float greenValue = (sin(timeValue) / 2.0f) + 0.5f; +int vertexColorLocation = <function id='45'>glGetUniformLocation</function>(shaderProgram, "ourColor"); +<function id='28'>glUseProgram</function>(shaderProgram); +<function id='44'>glUniform</function>4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); +</code></pre> + + <p> + First, we retrieve the running time in seconds via <fun><function id='47'>glfwGetTime</function>()</fun>. Then we vary the color in the range of <code>0.0</code> - <code>1.0</code> by using the <fun>sin</fun> function and store the result in <var>greenValue</var>. +最初に<fun><function id='47'>glfwGetTime</function>()</fun>により実行時間を秒単位で取得します。次に<fun>sin</fun>関数を利用し色の数値を<code>0.0</code>と<code>1.0</code>の間で設定し、その値を<var>greenValue</var>に保存します。 + </p> + + <p> + Then we query for the location of the <var>ourColor</var> uniform using <fun><function id='45'>glGetUniformLocation</function></fun>. We supply the shader program and the name of the uniform (that we want to retrieve the location from) to the query function. If <fun><function id='45'>glGetUniformLocation</function></fun> returns <code>-1</code>, it could not find the location. Lastly we can set the uniform value using the <fun><function id='44'>glUniform</function>4f</fun> function. Note that finding the uniform location does not require you to use the shader program first, but updating a uniform <strong>does</strong> require you to first use the program (by calling <fun><function id='28'>glUseProgram</function></fun>), because it sets the uniform on the currently active shader program. +そして<var>ourColor</var>ユニフォームの場所を<fun><function id='45'>glGetUniformLocation</function></fun>により割り出します。シェーダープログラムと、場所が知りたいユニフォームの名前をこの関数に渡します。<fun><function id='45'>glGetUniformLocation</function></fun>が<code>-1</code>を返した場合、ユニフォームが見つからなかったということです。最後に<fun><function id='44'>glUniform</function>4f</fun>を用いてユニフォームの値を設定します。ユニフォームを探す際にシェーダープログラムの利用を宣言する必要はありません。しかしユニフォームの値の更新は、現在アクティブなシェーダープログラムに対して行われるので、<fun><function id='28'>glUseProgram</function></fun>によりシェーダープログラムの利用を宣言することが<strong>必要</strong>です。このことに留意してください。 + </p> + +<note> +<p> + Because OpenGL is in its core a C library it does not have native support for function overloading, so wherever a function can be called with different types OpenGL defines new functions for each type required; <fun><function id='44'>glUniform</function></fun> is a perfect example of this. The function requires a specific postfix for the type of the uniform you want to set. A few of the possible postfixes are: +OpenGLの核となる部分はC言語のライブラリであり、関数のオーバーロードができないので、違う型の引数に対して同じことを行う関数が必要な場合、それぞれの型に対して別の関数として定義されています。<fun><function id='44'>glUniform</function></fun>はそのいい例です。値を設定するユニフォームの型に応じて、関数名に接尾語がつきます。例として以下のようなものが挙げられます: + <ul> + <li><code>f</code>: the function expects a <code>float</code> as its value.</li> + <li><code>i</code>: the function expects an <code>int</code> as its value.</li> + <li><code>ui</code>: the function expects an <code>unsigned int</code> as its value.</li> + <li><code>3f</code>: the function expects 3 <code>float</code>s as its value.</li> + <li><code>fv</code>: the function expects a <code>float</code> vector/array as its value.</li> + </ul> + <ul> + <li><code>f</code>: 関数は1つの<code>float</code>を引数に取ります。</li> + <li><code>i</code>: 関数は1つの<code>int</code>を引数に取ります。</li> + <li><code>ui</code>: 関数は1つの<code>unsigned int</code>を引数に取ります。</li> + <li><code>3f</code>: 関数は3つの<code>float</code>を引数に取ります。</li> + <li><code>fv</code>: 関数は<code>float</code>のベクトル(配列)1つを引数に取ります。</li> + </ul> + Whenever you want to configure an option of OpenGL simply pick the overloaded function that corresponds with your type. In our case we want to set 4 floats of the uniform individually so we pass our data via <fun><function id='44'>glUniform</function>4f</fun> (note that we also could've used the <code>fv</code> version). +OpenGLのオプションを設定したい場合、単にオーバーロードされた関数から適合する型のものを呼び出して下さい。今回はユニフォームに対して4つの浮動小数点数を割り当てたいので<fun><function id='44'>glUniform</function>4f</fun>を利用します(色を配列に格納すれば<code>fv</code>を利用することも可能です)。 +</p> +</note> + + <p> + Now that we know how to set the values of uniform variables, we can use them for rendering. If we want the color to gradually change, we want to update this uniform every frame, otherwise the triangle would maintain a single solid color if we only set it once. So we calculate the <var>greenValue</var> and update the uniform each render iteration: +ユニフォーム変数に値をセットしたので、これを描画に利用できるようになりました。時間と共に色を変化させるには、フレーム毎にユニフォームを更新しなければなりません。そうしないと三角形は最初に設定した色のままになります。そのため<var>greenValue</var>の計算とユニフォームの更新は描画ループの中に配置します: + </p> + +<pre><code> +while(!<function id='14'>glfwWindowShouldClose</function>(window)) +{ + // input + // 入力 + processInput(window); + + // render + // 描画 + // clear the colorbuffer + // 色バッファの削除 + <function id='13'><function id='10'>glClear</function>Color</function>(0.2f, 0.3f, 0.3f, 1.0f); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT); + + // be sure to activate the shader + // シェーダーのアクティベート + <function id='28'>glUseProgram</function>(shaderProgram); + + // update the uniform color + // ユニフォームの色を更新 + float timeValue = <function id='47'>glfwGetTime</function>(); + float greenValue = sin(timeValue) / 2.0f + 0.5f; + int vertexColorLocation = <function id='45'>glGetUniformLocation</function>(shaderProgram, "ourColor"); + <function id='44'>glUniform</function>4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); + + // now render the triangle + // 三角形の描画 + <function id='27'>glBindVertexArray</function>(VAO); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 3); + + // swap buffers and poll IO events + // バッファの入替えとイベントの処理 + <function id='24'>glfwSwapBuffers</function>(window); + <function id='23'>glfwPollEvents</function>(); +} +</code></pre> + + <p> + The code is a relatively straightforward adaptation of the previous code. This time, we update a uniform value each frame before drawing the triangle. If you update the uniform correctly you should see the color of your triangle gradually change from green to black and back to green. +コードには以前のものをほとんどそのまま適応しただけです。今回は各フレームにおいて三角形を描画する前にユニフォームの値を更新しています。ユニフォームの更新がうまく行われていれば、三角形の色が緑から黒へ、黒から緑へ変化していくのが確認できるでしょう。 + </p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/shaders.mp4" type="video/mp4" /> + <img src="/img/getting-started/shaders2.png" class="clean"/> + </video> +</div> + + +<p> + Check out the source code <a href="/code_viewer_gh.php?code=src/1.getting_started/3.1.shaders_uniform/shaders_uniform.cpp" target="_blank">here</a> if you're stuck. +問題があれば<a href="/code_viewer_gh.php?code=src/1.getting_started/3.1.shaders_uniform/shaders_uniform.cpp" target="_blank">ここ</a>でソースコードを確認してください。 + </p> + +<p> + As you can see, uniforms are a useful tool for setting attributes that may change every frame, or for interchanging data between your application and your shaders, but what if we want to set a color for each vertex? In that case we'd have to declare as many uniforms as we have vertices. A better solution would be to include more data in the vertex attributes which is what we're going to do now. +ご覧のように、ユニフォームはフレームごとに値を変更したり、アプリケーションとシェーダーの間でデータをやりとりするうえで便利です。しかし各頂点に対してそれぞれの色を設定したい場合はどうでしょう。この場合、頂点の数だけユニフォームを宣言する必要があります。より良い方法としては頂点属性に追加の情報を含めることが挙げられます。以下、その方法を見ていきましょう。 + </p> + + <h2>More attributes!</h2> + <h2>属性の追加</h2> + <p> + We saw in the previous chapter how we can fill a VBO, configure vertex attribute pointers and store it all in a VAO. This time, we also want to add color data to the vertex data. We're going to add color data as 3 <code>float</code>s to the <var>vertices</var> array. We assign a red, green and blue color to each of the corners of our triangle respectively: +前章で、VBOにデータを割り当て、頂点属性のポインタを設定し、それをVAOに保存する方法を説明しました。ここでは頂点データに色の情報も追加しましょう。<var>頂点</var>配列に色の情報を3つの<code>float</code>として追加します。三角形の各頂点に赤、緑、青の色を割り当てましょう: + </p> + +<pre><code> +float vertices[] = { + // positions // colors + // 位置 // 色 + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 + -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 上 +}; +</code></pre> + +<p> + Since we now have more data to send to the vertex shader, it is necessary to adjust the vertex shader to also receive our color value as a vertex attribute input. Note that we set the location of the <var>aColor</var> attribute to 1 with the layout specifier: +頂点シェーダーに送信するデータが増えたため、頂点シェーダーが頂点属性の入力から色の情報を受け取れるように設定する必要があります。<var>aColor</var>の属性位置を1に設定していることに注意して下さい: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; // the position variable has attribute position 0 +layout (location = 1) in vec3 aColor; // the color variable has attribute position 1 +layout (location = 0) in vec3 aPos; // 位置の変数の属性位置は0 +layout (location = 1) in vec3 aColor; // 色の変数の属性位置は1 + +out vec3 ourColor; // output a color to the fragment shader +out vec3 ourColor; // フラグメントシェーダーへ色を出力 + +void main() +{ + gl_Position = vec4(aPos, 1.0); + ourColor = aColor; // set ourColor to the input color we got from the vertex data + ourColor = aColor; // ourColorを頂点データから取得した色に設定 +} +</code></pre> + + <p> + Since we no longer use a uniform for the fragment's color, but now use the <var>ourColor</var> output variable we'll have to change the fragment shader as well: +フラグメントの色としてユニフォームではなく<var>ourColor</var>の出力を利用するので、フラグメントシェーダーを以下のように変更します: + </p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec3 ourColor; + +void main() +{ + FragColor = vec4(ourColor, 1.0); +} +</code></pre> + +<p> + Because we added another vertex attribute and updated the VBO's memory we have to re-configure the vertex attribute pointers. The updated data in the VBO's memory now looks a bit like this: +頂点属性を追加し、VBOのメモリを更新したので、頂点属性ポインタを再設定します。更新されたデータはVBOのメモリ内で以下のような配置になっています: + </p> + + <img src="/img/getting-started/vertex_attribute_pointer_interleaved.png" class="clean" alt="Interleaved data of position and color within VBO to be configured wtih <function id='30'>glVertexAttribPointer</function>"/> + +<p> + Knowing the current layout we can update the vertex format with <fun><function id='30'>glVertexAttribPointer</function></fun>: +これお踏まえ、頂点のフォーマットを<fun><function id='30'>glVertexAttribPointer</function></fun>により以下のように変更します: +</p> + +<pre><code> +// position attribute +// 位置属性 +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +// color attribute +// 色属性 +<function id='30'>glVertexAttribPointer</function>(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float))); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(1); +</code></pre> + +<p> + The first few arguments of <fun><function id='30'>glVertexAttribPointer</function></fun> are relatively straightforward. This time we are configuring the vertex attribute on attribute location <code>1</code>. The color values have a size of <code>3</code> <code>float</code>s and we do not normalize the values. +<fun><function id='30'>glVertexAttribPointer</function></fun>の前半の引数は分かりやすいと思います。今回は、属性位置<code>1</code>の頂点属性を設定します。色の情報は<code>float</code>が<code>3</code>つ分のサイズで、正規化はしません。 + </p> + + <p> + Since we now have two vertex attributes we have to re-calculate the <em>stride</em> value. To get the next attribute value (e.g. the next <code>x</code> component of the position vector) in the data array we have to move <code>6</code> <code>float</code>s to the right, three for the position values and three for the color values. This gives us a stride value of 6 times the size of a <code>float</code> in bytes (= <code>24</code> bytes). <br/> + Also, this time we have to specify an offset. For each vertex, the position vertex attribute is first so we declare an offset of <code>0</code>. The color attribute starts after the position data so the offset is <code>3 * sizeof(float)</code> in bytes (= <code>12</code> bytes). +頂点属性が二種類になったので<em>ストライド</em>も計算しなおします。次の属性の値(つまり、次の位置属性の<code>x</code>要素)を得るために、データの配列上で<code>float</code><code>6</code>つ分右に移動する必要があります。位置属性の値3つ分と色属性の値3つ分です。つまりストライドは<code>float</code>のサイズの6倍(=<code>24</code>バイト)です。<br/> +加えて今回はデータの開始位置も設定しないといけません。各頂点において、位置を表わす頂点属性は先頭に位置するので、開始位置は<code>0</code>です。色を表わす頂点属性は位置のデータの後ろにあるので、開始位置は<code>3 * sizeof(float)</code>バイト(=<code>12</code>バイト)です。 +</p> + +<p> + Running the application should result in the following image: +アプリケーションを実行すると以下のようになります: +</p> + + <img src="/img/getting-started/shaders3.png" class="clean"/> + + <p> + Check out the source code <a href="/code_viewer_gh.php?code=src/1.getting_started/3.2.shaders_interpolation/shaders_interpolation.cpp" target="_blank">here</a> if you're stuck. +問題があれば<a href="/code_viewer_gh.php?code=src/1.getting_started/3.2.shaders_interpolation/shaders_interpolation.cpp" target="_blank">ここ</a>でソースコードを確認して下さい。 + </p> + + <p> + The image may not be exactly what you would expect, since we only supplied 3 colors, not the huge color palette we're seeing right now. This is all the result of something called <def>fragment interpolation</def> in the fragment shader. When rendering a triangle the rasterization stage usually results in a lot more fragments than vertices originally specified. The rasterizer then determines the positions of each of those fragments based on where they reside on the triangle shape.<br/> + Based on these positions, it <def>interpolates</def> all the fragment shader's input variables. Say for example we have a line where the upper point has a green color and the lower point a blue color. If the fragment shader is run at a fragment that resides around a position at <code>70%</code> of the line, its resulting color input attribute would then be a linear combination of green and blue; to be more precise: <code>30%</code> blue and <code>70%</code> green. +画像は読者が想像したものと少し違うと思います。3つの色を設定したのであり、この画像のようにたくさんの色のパターンを指定したわけではないからです。この結果はフラグメントシェーダーの<def>フラグメント補完</def>と呼ばれる機能によるものです。三角形を描画する時、ラスタリゼーションステージでは最初に用意した頂点の数より多くのフラグメントが生成されています。ラスタライザはそれらのフラグメントの位置を三角形内での位置に基づいて決定します。<br/> +この位置に基づき、フラグメントシェーダーの入力が<def>補完</def>されます。例えばここに一本の線があり、上端が緑で下端が青だとしましょう。この線上の下から70%の位置にあるフラグメントに対してフラグメントシェーダーが実行された場合、このフラグメントの色は緑と青の線形補完になります。もう少し詳しく言うと、<code>30%</code>の青と<code>70%</code>の緑になります。 + </p> + + <p> + This is exactly what happened at the triangle. We have 3 vertices and thus 3 colors, and judging from the triangle's pixels it probably contains around 50000 fragments, where the fragment shader interpolated the colors among those pixels. If you take a good look at the colors you'll see it all makes sense: red to blue first gets to purple and then to blue. Fragment interpolation is applied to all the fragment shader's input attributes. +これがまさに三角形で起こったことです。3つの頂点と3つの色があります。三角形のピクセル数から考えて、おそらくフラグメントは50000個程度存在します。このフラグメントそれぞれに対してフラグメントシェーダーが色を補完します。三角形の色をよくみると理解できるはずです。赤から青への変化は途中で紫を経由しています。フラグメント補完はフラグメントシェーダーの入力となる属性すべてに対して適応されます。 + </p> + +<h1>Our own shader class</h1> +<h1>独自のシェーダークラス</h1> + <p> + Writing, compiling and managing shaders can be quite cumbersome. As a final touch on the shader subject we're going to make our life a bit easier by building a shader class that reads shaders from disk, compiles and links them, checks for errors and is easy to use. This also gives you a bit of an idea how we can encapsulate some of the knowledge we learned so far into useful abstract objects. +シェーダーの作成、コンパイルそして管理は面倒です。シェーダーの章の最後として、独自のシェーダークラスを作成し、扱いやすくしましょう。このシェーダークラスはシェーダーをディスクから読み込み、コンパイル、リンクし、エラーを検出するものです。こうすることでここまで学んだ知識を抽象的なオブジェクトに詰め込み利便性を高める方法にも慣れることができます。 + </p> + + <p> + We will create the shader class entirely in a header file, mainly for learning purposes and portability. Let's start by adding the required includes and by defining the class structure: +学習のため、そして移植性を高めるため、シェーダークラスをひとつのヘッダーファイルに構築します。まずはじめに必要なものをインクルードしクラスの構造を定義しましょう: + </p> + +<pre><code> +#ifndef SHADER_H +#define SHADER_H + +#include &lt;glad/glad.h&gt; // include glad to get all the required OpenGL headers +#include &lt;glad/glad.h&gt; // gladをインクルードして必要なOpenGLヘッダーを取得 + +#include &lt;string&gt; +#include &lt;fstream&gt; +#include &lt;sstream&gt; +#include &lt;iostream&gt; + + +class Shader +{ +public: + // the program ID + // プログラムID + unsigned int ID; + + // constructor reads and builds the shader + // コンストラクタがシェーダーを読み込んでビルド + Shader(const char* vertexPath, const char* fragmentPath); + // use/activate the shader + // シェーダーをアクティベート + void use(); + // utility uniform functions + // ユニフォーム設定用関数 + void setBool(const std::string &name, bool value) const; + void setInt(const std::string &name, int value) const; + void setFloat(const std::string &name, float value) const; +}; + +#endif +</code></pre> + + <note> + We used several <def>preprocessor directives</def> at the top of the header file. Using these little lines of code informs your compiler to only include and compile this header file if it hasn't been included yet, even if multiple files include the shader header. This prevents linking conflicts. +ヘッダファイルの先頭で<def>プリプロセッサ・ディレクティブ</def>を利用しています。これは複数のファイルからこのヘッダをインクルードしようとした時でも、このファイルがまだインクルードされていない場合に限りインクルードしてコンパイルするようにコンパイラに伝えるものです。これによりリンク時の競合を防げます。 + </note> + + <p> + The shader class holds the ID of the shader program. Its constructor requires the file paths of the source code of the vertex and fragment shader respectively that we can store on disk as simple text files. To add a little extra we also add several utility functions to ease our lives a little: <fun>use</fun> activates the shader program, and all <fun>set...</fun> functions query a uniform location and set its value. +シェーダークラスはシェーダープログラムのIDを記憶しています。頂点シェーダーとフラグメントシェーダーはディスク上にテキストファイルとして保存しておきます。シェーダークラスのコンストラクタはこれらのシェーダーのソースコードのファイルパスを必要とします。さらに利便性を高めるためいくつかの関数を追加します。シェーダープログラムをアクティベートする<fun>use</fun>関数と、ユニフォームの場所を特定してその数値を変更する<fun>set...</fun>関数です。 + </p> + +<h2>Reading from file</h2> +<h2>ファイルからの読込み</h2> +<p> + We're using C++ filestreams to read the content from the file into several <code>string</code> objects: +ファイルの内容を読み込み<code>string</code>オブジェクトに格納するため、C++のファイルストリームを利用します: +</p> + +<pre><code> +Shader(const char* vertexPath, const char* fragmentPath) +{ + // 1. retrieve the vertex/fragment source code from filePath + // 1. 頂点シェーダーとフラグメントシェーダーのソースコードをfilePathから読込み + std::string vertexCode; + std::string fragmentCode; + std::ifstream vShaderFile; + std::ifstream fShaderFile; + // ensure ifstream objects can throw exceptions: + // ifstreamオブジェクトがエラーを出せるかどうか確認: + vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + try + { + // open files + // ファイルを開く + vShaderFile.open(vertexPath); + fShaderFile.open(fragmentPath); + std::stringstream vShaderStream, fShaderStream; + // read file's buffer contents into streams + // ファイルのバッファをストリームに読込み + vShaderStream &lt;&lt; vShaderFile.rdbuf(); + fShaderStream &lt;&lt; fShaderFile.rdbuf(); + // close file handlers + // ファイルを閉じる + vShaderFile.close(); + fShaderFile.close(); + // convert stream into string + // ストリームを文字列に変換 + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + } + catch(std::ifstream::failure e) + { + std::cout &lt;&lt; "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" &lt;&lt; std::endl; + } + const char* vShaderCode = vertexCode.c_str(); + const char* fShaderCode = fragmentCode.c_str(); + [...] +</code></pre> + + <p> + Next we need to compile and link the shaders. Note that we're also reviewing if compilation/linking failed and if so, print the compile-time errors. This is extremely useful when debugging (you are going to need those error logs eventually): +続いてシェーダーをコンパイル及びリンクします。加えて、コンパイル及びリンクが成功したかどうか確認し、失敗していた場合コンパイル時のエラーを表示させています。これはデバッグ時に非常に便利です(開発においてエラーログは必須です)。 + </p> + +<pre><code> +// 2. compile shaders +// 2. シェーダーのコンパイル +unsigned int vertex, fragment; +int success; +char infoLog[512]; + +// vertex Shader +// 頂点シェーダー +vertex = <function id='37'>glCreateShader</function>(GL_VERTEX_SHADER); +<function id='42'>glShaderSource</function>(vertex, 1, &amp;vShaderCode, NULL); +<function id='38'>glCompileShader</function>(vertex); +// print compile errors if any +// コンパイルエラーの表示 +<function id='39'>glGetShaderiv</function>(vertex, GL_COMPILE_STATUS, &amp;success); +if(!success) +{ + <function id='40'>glGetShaderInfoLog</function>(vertex, 512, NULL, infoLog); + std::cout &lt;&lt; "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" &lt;&lt; infoLog &lt;&lt; std::endl; +}; + +// similiar for Fragment Shader +// フラグメントシェーダーに対しても同様 +[...] + +// shader Program +// シェーダープログラム +ID = <function id='36'>glCreateProgram</function>(); +<function id='34'>glAttachShader</function>(ID, vertex); +<function id='34'>glAttachShader</function>(ID, fragment); +<function id='35'>glLinkProgram</function>(ID); +// print linking errors if any +<function id='41'>glGetProgramiv</function>(ID, GL_LINK_STATUS, &amp;success); +if(!success) +{ + glGetProgramInfoLog(ID, 512, NULL, infoLog); + std::cout &lt;&lt; "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" &lt;&lt; infoLog &lt;&lt; std::endl; +} + +// delete the shaders as they're linked into our program now and no longer necessary +// リンクが終わりで必要がなくなったシェーダーを削除。 +<function id='46'>glDeleteShader</function>(vertex); +<function id='46'>glDeleteShader</function>(fragment); +</code></pre> + + <p> + The <fun>use</fun> function is straightforward: + </p> + +<pre><code> +void use() +{ + <function id='28'>glUseProgram</function>(ID); +} +</code></pre> + +<p> + Similarly for any of the uniform setter functions: +</p> + +<pre><code> +void setBool(const std::string &name, bool value) const +{ + <function id='44'>glUniform</function>1i(<function id='45'>glGetUniformLocation</function>(ID, name.c_str()), (int)value); +} +void setInt(const std::string &name, int value) const +{ + <function id='44'>glUniform</function>1i(<function id='45'>glGetUniformLocation</function>(ID, name.c_str()), value); +} +void setFloat(const std::string &name, float value) const +{ + <function id='44'>glUniform</function>1f(<function id='45'>glGetUniformLocation</function>(ID, name.c_str()), value); +} +</code></pre> + + <p> + And there we have it, a completed <a href="/code_viewer_gh.php?code=includes/learnopengl/shader_s.h" target="_blank">shader class</a>. Using the shader class is fairly easy; we create a shader object once and from that point on simply start using it: +これで<a href="/code_viewer_gh.php?code=includes/learnopengl/shader_s.h" target="_blank">シェーダークラス</a>の構築は完了です。このクラスを利用するのは簡単です。シェーダーオブジェクトを作成するだけで使えるのです: + </p> + +<pre><code> +Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs"); +[...] +while(...) +{ + ourShader.use(); + ourShader.setFloat("someUniform", 1.0f); + DrawStuff(); +} +</code></pre> + +<p> + Here we stored the vertex and fragment shader source code in two files called <code>shader.vs</code> and <code>shader.fs</code>. You're free to name your shader files however you like; I personally find the extensions <code>.vs</code> and <code>.fs</code> quite intuitive. +それでは頂点シェーダーとフラグメントシェーダーを<code>shader.vs</code>と<code>shader.fs</code>というファイルにそれぞれ保存しましょう。ファイルの名前は好きなものにしてください。個人的に<code>.vs</code>と<code>.fs</code>という拡張子が分かりやすいのでこのような名前にしています。 +</p> + + <p> + You can find the source code <a href="/code_viewer_gh.php?code=src/1.getting_started/3.3.shaders_class/shaders_class.cpp" target="_blank">here</a> using our newly created <a href="/code_viewer_gh.php?code=includes/learnopengl/shader_s.h" target="_blank">shader class</a>. Note that you can click the shader file paths to find the shaders' source code. +今作った<a href="/code_viewer_gh.php?code=includes/learnopengl/shader_s.h" target="_blank">シェーダークラス</a>を利用したソースコードは<a href="/code_viewer_gh.php?code=src/1.getting_started/3.3.shaders_class/shaders_class.cpp" target="_blank">ここ</a>にあります。シェーダーファイルのパスをクリックすることでシェーダーのソースコードにもアクセスできます。 + </p> + +<h1>Exercises</h1> +<h1>演習</h1> + <ol> + <li>Adjust the vertex shader so that the triangle is upside down: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.4.shaders_exercise1/shaders_exercise1.cpp" target="_blank">solution</a>.</li> + <li>頂点シェーダーを変更して三角形を逆さに表示してください: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.4.shaders_exercise1/shaders_exercise1.cpp" target="_blank">回答</a></li> + <li>Specify a horizontal offset via a uniform and move the triangle to the right side of the screen in the vertex shader using this offset value: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.5.shaders_exercise2/shaders_exercise2.cpp" target="_blank">solution</a>.</li> + <li>水平方向の位置をユニフォームで設定し、この値を頂点シェーダーから利用して三角形をスクリーンの右に寄せて下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.5.shaders_exercise2/shaders_exercise2.cpp" target="_blank">solution</a>.</li> + <li>Output the vertex position to the fragment shader using the <code>out</code> keyword and set the fragment's color equal to this vertex position (see how even the vertex position values are interpolated across the triangle). Once you managed to do this; try to answer the following question: why is the bottom-left side of our triangle black?: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.6.shaders_exercise3/shaders_exercise3.cpp" target="_blank">solution</a>.</li> + <li><code>out</code>キーワードを用いて頂点の座標をフラグメントシェーダーに出力し、フラグメントの色をその座標と同じになるようにして下さい(頂点の座標であっても補完されることを確認して下さい)。これができたら、左下がどうして黒くなっているのか考えて下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/3.6.shaders_exercise3/shaders_exercise3.cpp" target="_blank">solution</a>.</li> + </ol> + + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Textures.html b/pub/Getting-started/Textures.html @@ -0,0 +1,954 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Textures</h1> + <h1 id="content-title">テクスチャ</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Textures</h1> +<p> + We learned that to add more detail to our objects we can use colors for each vertex to create some interesting images. However, to get a fair bit of realism we'd have to have many vertices so we could specify a lot of colors. This takes up a considerable amount of extra overhead, since each model needs a lot more vertices and for each vertex a color attribute as well. +物体にディティールを追加して面白い画像を作るための方法として各頂点に色を着ける方法を学習しました。しかし、現実的な物体を描くためには大量の頂点を作成してそれぞれに色を指定しなければなりません。この方法では各モデルが大量の頂点と色の情報を必要とするので、処理にかかる負荷が非常に大きくなります。 +</p> +<p> + What artists and programmers generally prefer is to use a <def>texture</def>. A texture is a 2D image (even 1D and 3D textures exist) used to add detail to an object; think of a texture as a piece of paper with a nice brick image (for example) on it neatly folded over your 3D house so it looks like your house has a stone exterior. Because we can insert a lot of detail in a single image, we can give the illusion the object is extremely detailed without having to specify extra vertices. +アーティストやプログラマは一般には<def>テクスチャ</def>を利用する方法を好みます。テクスチャは二次元の画像で、物体にディティールを与えるために利用されます(一次元や三次元のテクスチャも存在します)。例えばお洒落なレンガが描かれた紙を想像してみてください。これが三次元の家を包んでいれば、その家はレンガ作りに見えるでしょう。一つの画像の中に多くのディティールを詰め込めるので、頂点を追加せずとも細部まで作りこまれているかのように見せることができるのです。 +</p> + +<note> + Next to images, textures can also be used to store a large collection of arbitrary data to send to the shaders, but we'll leave that for a different topic. +テクスチャは画像に加え任意のデータを頂点シェーダーに送信するのに使えます。しかしこの話は別のトピックスとして取っておきます。 +</note> + +<p> + Below you'll see a texture image of a <a href="/img/textures/wall.jpg" target="_blank">brick wall</a> mapped to the triangle from the previous chapter. +以下に<a href="/img/textures/wall.jpg" target="_blank">レンガの壁</a>のテクスチャを貼り付けた三角形を示します。 +</p> + +<img src="/img/getting-started/textures.png" class="clean"/> + +<p> +In order to map a texture to the triangle we need to tell each vertex of the triangle which part of the texture it corresponds to. Each vertex should thus have a <def>texture coordinate</def> associated with them that specifies what part of the texture image to sample from. Fragment interpolation then does the rest for the other fragments. +三角形にテクスチャを貼り付けるには、各頂点がテクスチャのどの位置に対応するのかを指定する必要があります。そのため各頂点にはテクスチャ画像のどの部分に対応するかを示した<def>テクスチャ座標</def>が必要です。そうすれば他のフラグメントに対してはフラグメント補完がテクスチャを割り当ててくれます。 +</p> + +<p> + Texture coordinates range from <code>0</code> to <code>1</code> in the <code>x</code> and <code>y</code> axis (remember that we use 2D texture images). Retrieving the texture color using texture coordinates is called <def>sampling</def>. Texture coordinates start at <code>(0,0)</code> for the lower left corner of a texture image to <code>(1,1)</code> for the upper right corner of a texture image. The following image shows how we map texture coordinates to the triangle: +テクスチャ座標は<code>x</code>座標と<code>y</code>座標があり(テクスチャは二次元です)、それぞれ<code>0</code>から<code>1</code>の範囲で指定します。テクスチャ座標によりテクスチャの色を抽出することは<def>サンプリング</def>と呼ばれます。テクスチャ座標はテクスチャ画像の左下が<code>(0, 0)</code>で、右上が<code>(1, 1)</code>です。以下の画像はどのようにテクスチャ座標を三角形に対応させるかを示したものです: +</p> + +<img src="/img/getting-started/tex_coords.png"/> + +<p> + We specify 3 texture coordinate points for the triangle. We want the bottom-left side of the triangle to correspond with the bottom-left side of the texture so we use the <code>(0,0)</code> texture coordinate for the triangle's bottom-left vertex. The same applies to the bottom-right side with a <code>(1,0)</code> texture coordinate. The top of the triangle should correspond with the top-center of the texture image so we take <code>(0.5,1.0)</code> as its texture coordinate. We only have to pass 3 texture coordinates to the vertex shader, which then passes those to the fragment shader that neatly interpolates all the texture coordinates for each fragment. +三角形に対して3つのテクスチャ座標を割り当てます。三角形の左下をテクスチャの左下に対応させたいので、この頂点には<code>(0, 0)</code>というテクスチャ座標を割り当てます。同様に右下の頂点のテクスチャ座標は<code>(1, 0)</code>にします。上の頂点はテクスチャ画像の上端中央を対応させるため<code>(0.5, 1.0)</code>というテクスチャ座標を割り当てます。3つのテクスチャ座標を頂点シェーダーに渡せば十分です。あとのフラグメントに関しては、フラグメントシェーダーにおいてきちんと補完されます。 +</p> + +<p> + The resulting texture coordinates would then look like this: +その結果テクスチャ座標は以下のようになります: +</p> + +<pre><code> +float texCoords[] = { + 0.0f, 0.0f, // lower-left corner + 1.0f, 0.0f, // lower-right corner + 0.5f, 1.0f // top-center corner +}; +</code></pre> + +<p> + Texture sampling has a loose interpretation and can be done in many different ways. It is thus our job to tell OpenGL how it should <em>sample</em> its textures. +テクスチャのサンプリングと一口に言っても多くの方法があります。そのためOpenGLがテクスチャをどのように<em>サンプリング</em>するかは自分達で指定しなければなりません。 +</p> + +<h2>Texture Wrapping</h2> +<h2>テクスチャの繰り返し</h2> +<p> + Texture coordinates usually range from <code>(0,0)</code> to <code>(1,1)</code> but what happens if we specify coordinates outside this range? The default behavior of OpenGL is to repeat the texture images (we basically ignore the integer part of the floating point texture coordinate), but there are more options OpenGL offers: +テクスチャ座標は通常<code>(0, 0)</code>から<code>(1, 1)</code>までですが、この範囲外の座標を指定した場合なにが起こるでしょう。OpenGLのデフォルトの振舞いではテクスチャ画像を繰返します(テクスチャ座標の整数部分を基本的に無視します)が、他のオプションもあります: +</p> + + <ul> + <li><var>GL_REPEAT</var>: The default behavior for textures. Repeats the texture image.</li> + <li><var>GL_REPEAT</var>: デフォルトの振舞。テクスチャ画像を繰返す。</li> + <li><var>GL_MIRRORED_REPEAT</var>: Same as <var>GL_REPEAT</var> but mirrors the image with each repeat.</li> + <li><var>GL_MIRRORED_REPEAT</var>: テクスチャ画像を鏡写しに繰返す。</li> + <li><var>GL_CLAMP_TO_EDGE</var>: Clamps the coordinates between <code>0</code> and <code>1</code>. The result is that higher coordinates become clamped to the edge, resulting in a stretched edge pattern.</li> + <li><var>GL_CLAMP_TO_EDGE</var>: 座標を<code>0</code>と<code>1</code>の間に固定。範囲外の座標は端に固定され、縁を引き伸ばしたようになる。</li> + <li><var>GL_CLAMP_TO_BORDER</var>: Coordinates outside the range are now given a user-specified border color.</li> + <li><var>GL_CLAMP_TO_BORDER</var>: 範囲外の座標にはユーザーが定めた境界線の色が与えられる。</li> + </ul> + +<p> + Each of the options have a different visual output when using texture coordinates outside the default range. Let's see what these look like on a sample texture image (original image by Hólger Rezende): +各オプションはテクスチャ座標を用いたとき、範囲外においてそれぞれ違った見た目になります。どのような結果になるのか、サンプルのテクスチャ画像で検証してみましょう(テクスチャ画像はHólger Rezende氏作): +</p> + +<img src="/img/getting-started/texture_wrapping.png" class="clean"/> + +<p> + Each of the aforementioned options can be set per coordinate axis (<code>s</code>, <code>t</code> (and <code>r</code> if you're using 3D textures) equivalent to <code>x</code>,<code>y</code>,<code>z</code>) with the <fun><function id='15'>glTexParameter</function>*</fun> function: +前述のオプションは<fun><function id='15'>glTexParameter</function>*</fun>により各座標軸毎に指定することも可能です(<code>s</code>、<code>t</code>(三次元のテクスチャの場合、<code>r</code>も)がそれぞれ<code>x</code>、<code>y</code>(、<code>z</code>)に対応)。 +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); +</code></pre> + +<p> + The first argument specifies the texture target; we're working with 2D textures so the texture target is <var>GL_TEXTURE_2D</var>. The second argument requires us to tell what option we want to set and for which texture axis; we want to configure it for both the <code>S</code> and <code>T</code> axis. The last argument requires us to pass in the texture wrapping mode we'd like and in this case OpenGL will set its texture wrapping option on the currently active texture with <var>GL_MIRRORED_REPEAT</var>. +最初の引数はテクスチャのターゲットです。ここでは二次元のテクスチャを利用しているのでこのターゲットは<var>GL_TEXTURE_2D</var>です。二番目の引数はどのオプションをどの座標軸に対して設定するのかを指定します。この例では<code>S</code>軸と<code>T</code>軸の両方を設定しています。最後の引数はテクスチャの繰り返し方を指定します。今回の場合、OpenGLは現在アクティブなテクスチャの繰り返し方を<var>GL_MIRRORED_REPEAT</var>にしています。 +</p> + +<p> + If we choose the <var>GL_CLAMP_TO_BORDER</var> option we should also specify a border color. This is done using the <code>fv</code> equivalent of the <fun><function id='15'>glTexParameter</function></fun> function with <var>GL_TEXTURE_BORDER_COLOR</var> as its option where we pass in a float array of the border's color value: +<var>GL_CLAMP_TO_BORDER</var>を選択した場合、境界線の色も指定しなければなりません。これは<fun><function id='15'>glTexParameter</function></fun>の<code>fv</code>版にオプションとして<var>GL_TEXTURE_BORDER_COLOR</var>を指定し、境界線の色を浮動小数点数の配列として渡すことで行えます。 +</p> + +<pre><code> +float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f }; +<function id='15'>glTexParameter</function>fv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); +</code></pre> + +<h2>Texture Filtering</h2> +<h2>テクスチャフィルタリング</h2> +<p> + Texture coordinates do not depend on resolution but can be any floating point value, thus OpenGL has to figure out which texture pixel (also known as a <def>texel</def> ) to map the texture coordinate to. This becomes especially important if you have a very large object and a low resolution texture. You probably guessed by now that OpenGL has options for this <def>texture filtering</def> as well. There are several options available but for now we'll discuss the most important options: <var>GL_NEAREST</var> and <var>GL_LINEAR</var>. + テクスチャ座標は解像度に依存せず、任意の浮動小数点数で指定できます。そのためOpenGLはテクスチャのピクセル(<def>テクセル</def>)をどのようにテクスチャ座標に割り当てるかを決定しなければなりません。これは大きな物体に対して低解像度のテクスチャを割り当てる際に特に重要です。この<def>テクスチャフィルタリング</def>と呼ばれる操作に対してもOpenGLがオブションを用意しているのではないかと思われるかもしれません。予想通りいくつかのオプションがありますが、ここでは中でも重要な<var>GL_NEAREST</var>と<var>GL_LINEAR</var>を紹介します。 +</p> + +<p> + <var>GL_NEAREST</var> (also known as <def>nearest neighbor</def> or <def>point</def> filtering) is the default texture filtering method of OpenGL. When set to <var>GL_NEAREST</var>, OpenGL selects the texel that center is closest to the texture coordinate. Below you can see 4 pixels where the cross represents the exact texture coordinate. The upper-left texel has its center closest to the texture coordinate and is therefore chosen as the sampled color: + <var>GL_NEAREST</var>(あるいは<def>最近傍</def>または<def>ポイント</def>フィルタリング)はOpenGLにおけるデフォルトのテクスチャフィルタリングの方法です。<var>GL_NEAREST</var>が設定されている場合、テクセルのうちその中心がテクスチャ座標に最も近いものが選択されます。以下の図は4つのピクセルとテクスチャ座標を十字で示したものです。左上のテクセルの中心がテクスチャ座標に最も近いので、このテクセルがサンプルとして選択されます: +</p> + + <img src="/img/getting-started/filter_nearest.png" class="clean"/> + +<p> + <var>GL_LINEAR</var> (also known as <def>(bi)linear filtering</def>) takes an interpolated value from the texture coordinate's neighboring texels, approximating a color between the texels. The smaller the distance from the texture coordinate to a texel's center, the more that texel's color contributes to the sampled color. Below we can see that a mixed color of the neighboring pixels is returned: + <var>GL_LINEAR</var>(あるいは<def>(双)線形フィルタリング</def>)はテクスチャ座標の周りにあるテクセルの色を近似することにより、周りのテクセルを補完した色を取ります。中心がテクスチャ座標に近いテクセルほどサンプルとして取られる色に対する寄与が大きくなります。以下の例から、周りのピクセルを混ぜた色が得られる様子が見てとれます: +</p> + +<img src="/img/getting-started/filter_linear.png" class="clean"/> + +<p> + But what is the visual effect of such a texture filtering method? Let's see how these methods work when using a texture with a low resolution on a large object (texture is therefore scaled upwards and individual texels are noticeable): + ところでこのテクスチャフィルタリングの方法が視覚的にどう影響するのでしょう。この手法が低解像度のテクスチャを大きな物体に利用した(つまりテクスチャは個々のテクセルが確認できるほど引き伸ばされます)様子を見てみましょう: +</p> + + <img src="/img/getting-started/texture_filtering.png" class="clean"/> + +<p> + <var>GL_NEAREST</var> results in blocked patterns where we can clearly see the pixels that form the texture while <var>GL_LINEAR</var> produces a smoother pattern where the individual pixels are less visible. <var>GL_LINEAR</var> produces a more realistic output, but some developers prefer a more 8-bit look and as a result pick the <var>GL_NEAREST</var> option. + <var>GL_NEAREST</var>はテクスチャを構成するピクセルがはっきりと見えるくらいかくかくした結果になりました。一方<var>GL_LINEAR</var>は個々のピクセルが見えにくくなめらかな仕上りになりす。<var>GL_LINEAR</var>はより現実的な見た目になりますが、開発者のなかには8-bitっぽい見た目を好み、<var>GL_NEAREST</var>を選択する人もいます。 + </p> + +<p> + Texture filtering can be set for <def>magnifying</def> and <def>minifying</def> operations (when scaling up or downwards) so you could for example use nearest neighbor filtering when textures are scaled downwards and linear filtering for upscaled textures. We thus have to specify the filtering method for both options via <fun><function id='15'>glTexParameter</function>*</fun>. The code should look similar to setting the wrapping method: + テクスチャフィルタリングは<def>拡大</def>と<def>縮小</def>の操作において設定できます。例えば縮小する場合には最近傍点フィルタリングを、拡大する際には線形フィルタリグをそれぞれ適応することができます。そのため両方に対して<fun><function id='15'>glTexParameter</function>*</fun>を用いてフィルタリング方式を設定する必要があります。そのようなコードはテクスチャの繰り返しを指定するコードと同様です: +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<h3>Mipmaps</h3> +<h3>ミップマップ</h3> +<p> + Imagine we had a large room with thousands of objects, each with an attached texture. There will be objects far away that have the same high resolution texture attached as the objects close to the viewer. Since the objects are far away and probably only produce a few fragments, OpenGL has difficulties retrieving the right color value for its fragment from the high resolution texture, since it has to pick a texture color for a fragment that spans a large part of the texture. This will produce visible artifacts on small objects, not to mention the waste of memory bandwidth using high resolution textures on small objects. + 部屋の中に数千もの物体があり、それぞれにテクスチャが割り当てられているのを想像してみてください。近くにある物体と同じような高解像度のテクスチャを割り当てられた物体が遠くにある場合もあります。そのような物体は、遠くにあるために少しのフラグメントしか生成しません。このようなフラグメントはテクスチャの広い範囲に渡り、そこから適切な色を取得しないといけないので、テクスチャが高解像度の場合OpenGLは苦労することになります。小さな物体に大きなテクスチャを利用するのは、メモリの帯域の無駄遣いであるだけでなく、視覚的にも奇妙な結果になります。 +</p> + +<p> + To solve this issue OpenGL uses a concept called <def>mipmaps</def> that is basically a collection of texture images where each subsequent texture is twice as small compared to the previous one. The idea behind mipmaps should be easy to understand: after a certain distance threshold from the viewer, OpenGL will use a different mipmap texture that best suits the distance to the object. Because the object is far away, the smaller resolution will not be noticeable to the user. OpenGL is then able to sample the correct texels, and there's less cache memory involved when sampling that part of the mipmaps. Let's take a closer look at what a mipmapped texture looks like: + この問題を解決するため、OpenGLは<def>ミップマップ</def>と呼ばれる概念を利用します。ミップマップとは、基本的にはテクスチャ画像の集合で、次の画像は前のものの半分の大きさになっています。ミップマップの考え方は簡単に理解できます。距離がある閾値を越えると、OpenGLがその距離に応じて適切なミップマップテクスチャを割り当てます。物体が遠くにあれば、解像度の低さにユーザーは気付きません。そうしてOpenGLは適切なテクセルをサンプリングでき、ミップマップの一部をサンプリングするうえでキャッシュメモリは少なくてすみます。テクスチャのミップマップがどのようなのか詳しく見てみましょう: +</p> + +<img src="/img/getting-started/mipmaps.png" class="clean"/> + +<p> + Creating a collection of mipmapped textures for each texture image is cumbersome to do manually, but luckily OpenGL is able to do all the work for us with a single call to <fun><function id='51'>glGenerateMipmap</function>s</fun> after we've created a texture. + ミップマップ化されたテクスチャを手動で生成するのは面倒ですが、テクスチャを作成したあと<fun><function id='51'>glGenerateMipmap</function>s</fun>を呼ぶだけでOpenGLがこの仕事を肩代りしてくれます。 +</p> + +<p> + When switching between mipmaps levels during rendering OpenGL may show some artifacts like sharp edges visible between the two mipmap layers. Just like normal texture filtering, it is also possible to filter between mipmap levels using <var>NEAREST</var> and <var>LINEAR</var> filtering for switching between mipmap levels. To specify the filtering method between mipmap levels we can replace the original filtering methods with one of the following four options: + 描画中ミップマップのサイズが切り替わる時にOpenGLは不自然な視覚効果を生じます。ミップマップのレイヤの間にくっきりした縁が見えたりするのです。通常のテクスチャフィルタリングと同様に、<var>NEAREST</var>と<var>LINEAR</var>フィルタリングにより、ミップマップレベルをフィルタリングできます。もとのフィルタリング方法を以下のいずれかのもので置き換えることで、ミップマップレベルの間のフィルタリング方法を指定できます: +</p> + + <ul> + <li><var>GL_NEAREST_MIPMAP_NEAREST</var>: takes the nearest mipmap to match the pixel size and uses nearest neighbor interpolation for texture sampling.</li> + <li><var>GL_NEAREST_MIPMAP_NEAREST</var>: ピクセルサイズに一番近いミップマップを取り、それを最近傍点補完によりサンプリング。</li> + <li><var>GL_LINEAR_MIPMAP_NEAREST</var>: takes the nearest mipmap level and samples that level using linear interpolation. </li> + <li><var>GL_LINEAR_MIPMAP_NEAREST</var>: ピクセルサイズに一番近いミップマップを取り、それを線形補完によりサンプリング。</li> + <li><var>GL_NEAREST_MIPMAP_LINEAR</var>: linearly interpolates between the two mipmaps that most closely match the size of a pixel and samples the interpolated level via nearest neighbor interpolation. </li> + <li><var>GL_NEAREST_MIPMAP_LINEAR</var>: ピクセルサイズに近いミップマップ二つを線形補完し、それを最近傍点補完によりサンプリング。</li> + <li><var>GL_LINEAR_MIPMAP_LINEAR</var>: linearly interpolates between the two closest mipmaps and samples the interpolated level via linear interpolation.</li> + <li><var>GL_LINEAR_MIPMAP_LINEAR</var>: ピクセルサイズに近いミップマップ二つを線形補間し、それを線形補間によりサンプリング。</li> + </ul> + +<p> + Just like texture filtering we can set the filtering method to one of the 4 aforementioned methods using <fun><function id='15'>glTexParameter</function>i</fun>: + テクスチャフィルタリングと同様に、フィルタリング方式を上記4つの中から選び、<fun><function id='15'>glTexParameter</function>i</fun>により設定できます: +</p> + +<pre><code> +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<p> + A common mistake is to set one of the mipmap filtering options as the magnification filter. This doesn't have any effect since mipmaps are primarily used for when textures get downscaled: texture magnification doesn't use mipmaps and giving it a mipmap filtering option will generate an OpenGL <var>GL_INVALID_ENUM</var> error code. + 拡大時のフィルタリング方法としてミップマップフィルタリングを設定するのはよくある間違いです。ミップマップはテクスチャが小さくなる時に利用されるものなので、拡大時のフィルタリングとして設定しても意味がありません。テクスチャの拡大にはミップマップを利用しないので、OpenGLが<var>GL_INVALID_ENUM</var>エラーを生成します。 +</p> + +<h1>Loading and creating textures</h1> +<h1>テクスチャの読み込みと作成</h1> +<p> + The first thing we need to do to actually use textures is to load them into our application. + Texture images can be stored in dozens of file formats, each with their own structure and ordering of data, so how do we get those images in our application? One solution would be to choose a file format we'd like to use, say <code>.PNG</code> and write our own image loader to convert the image format into a large array of bytes. While it's not very hard to write your own image loader, it's still cumbersome and what if you want to support more file formats? You'd then have to write an image loader for each format you want to support. + テクスチャを利用するにあたりまず必要なのは、それをアプリケーションに読み込むことです。テクスチャ画像は様々なフォーマットで保存でき、データの構造と配置順序はそれぞればらばらです。このような画像をアプリケーションに読み込むにはどうすればよいでしょうか。ひとつの方法として、例えば<code>.PNG</code>といったフォーマットを選び、そのフォーマットの画像を読み込むプログラムを独自に作成し、大きなバイト列に格納するというものが考えられます。このような読込み機を作成するのはそんなに難しいことではないですが、面倒ですし、それにもし、たくさんのフォーマットに対応したいとすればどうでしょう。それぞれのフォーマットに対応した読込み機をすべて自分で作成することになります。 +</p> + +<p> + Another solution, and probably a good one, is to use an image-loading library that supports several popular formats and does all the hard work for us. A library like <code>stb_image.h</code>. + 他のいい方法として、一般的なフォーマットをサポートした画像読込みライブラリを利用し、難儀な仕事を肩代わりさせるというものがあります。<code>stb_image.h</code>といったライブラリです。 +</p> + +<h2>stb_image.h</h2> +<p> + <code>stb_image.h</code> is a very popular single header image loading library by <a href="https://github.com/nothings" target="_blank">Sean Barrett</a> that is able to load most popular file formats and is easy to integrate in your project(s). <code>stb_image.h</code> can be downloaded from <a href="https://github.com/nothings/stb/blob/master/stb_image.h" target="_blank">here</a>. Simply download the single header file, add it to your project as <code>stb_image.h</code>, and create an additional C++ file with the following code: + <code>stb_image.h</code>は<a href="https://github.com/nothings" target="_blank">Sean Barrett</a>による、ヘッダひとつの有名なライブラリです。このライブラリは一般的なフォーマットのファイルを読み込め、簡単に自分のプロジェクトに組込めます。<code>stb_image.h</code>は<a href="https://github.com/nothings/stb/blob/master/stb_image.h" target="_blank">ここ</a>からダウンドードできます。ヘッダファイルをひとつダウンロードし、<code>stb_image.h</code>という名前でプロジェクトに追加し、以下のようなC++のファイルを作成するだけです: +</p> + +<pre><code> +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +</code></pre> + +<p> + By defining <var>STB_IMAGE_IMPLEMENTATION</var> the preprocessor modifies the header file such that it only contains the relevant definition source code, effectively turning the header file into a <code>.cpp</code> file, and that's about it. Now simply include <code>stb_image.h</code> somewhere in your program and compile. + <var>STB_IMAGE_IMPLEMENTATION</var>を定義することでプリプロセッサが、関係のある定義を記述したソースコードのみを含むようにヘッダファイルを変更し、それを効率よく<code>.cpp</code>ファイルに変更します。それではプログラムのどこかで<code>stb_image.h</code>をインクルードしコンパイルしましょう。 +</p> + +<p> + For the following texture sections we're going to use an image of a <a href="/img/textures/container.jpg" target="_blank">wooden container</a>. + To load an image using <code>stb_image.h</code> we use its <fun>stbi_load</fun> function: + 以降のテクスチャの章では<a href="/img/textures/container.jpg" target="_blank">木箱</a>の画像を利用します。<code>stb_image.h</code>により画像を読み込むには<fun>stbi_load</fun>関数を利用します: +</p> + +<pre><code> +int width, height, nrChannels; +unsigned char *data = stbi_load("container.jpg", &amp;width, &amp;height, &amp;nrChannels, 0); +</code></pre> + +<p> + The function first takes as input the location of an image file. It then expects you to give three <code>ints</code> as its second, third and fourth argument that <code>stb_image.h</code> will fill with the resulting image's <em>width</em>, <em>height</em> and <em>number</em> of color channels. We need the image's width and height for generating textures later on. <!--The last argument allows us to force a number of channels. Let's say the image has 4 channels (RGBA) and we only want to load the 3 color channels (RGB) without alpha, we set its last argument to <code>3</code>. --> + この関数は最初の引数に画像ファイルの場所を取ります。2番から4番の引数には3つの<code>int</code>を取り、そこに読み込んだ画像の<em>幅</em>、<em>高さ</em>、そして色チャネルの<em>数</em>が<code>stb_image.h</code>により格納されます。幅と高さはこの後テクスチャを作成するのに必要です。 +</p> + +<h2>Generating a texture</h2> +<h2>テクスチャの作成</h2> +<p> + Like any of the previous objects in OpenGL, textures are referenced with an ID; let's create one: + ここまで登場したOpenGLのオブジェクトと同様、テクスチャもIDによって参照されます。ひとつ作成してみましょう: +</p> + +<pre class="cpp"><code> +unsigned int texture; +<function id='50'>glGenTextures</function>(1, &amp;texture); +</code></pre> + +<p> + The <fun><function id='50'>glGenTextures</function></fun> function first takes as input how many textures we want to generate and stores them in a <code>unsigned int</code> array given as its second argument (in our case just a single <code>unsigned int</code>). Just like other objects we need to bind it so any subsequent texture commands will configure the currently bound texture: + <fun><function id='50'>glGenTextures</function></fun>関数は最初の入力として作成するテクスチャの数を受け取り、二番目の入力として与えられた<code>unsigned int</code>の配列にそれらを格納します(ここではひとつだけです)。他のオブジェクトと同様に、この後の設定がこのテクスチャに反映されるためにはこのテクスチャを紐付ける必要があります: +</p> + +<pre><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); +</code></pre> + +<p> + Now that the texture is bound, we can start generating a texture using the previously loaded image data. Textures are generated with <fun><function id='52'>glTexImage2D</function></fun>: + テクスチャを紐付けた後、先に読み込んでおいた画像を用いてテクスチャを生成することができます。テクスチャは<fun><function id='52'>glTexImage2D</function></fun>により生成されます: +</p> + +<pre class="cpp"><code> +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); +<function id='51'>glGenerateMipmap</function>(GL_TEXTURE_2D); +</code></pre> + +<p> + This is a large function with quite a few parameters so we'll walk through them step-by-step: + たくさんの引数を取る大きな関数なので一つづつ見ていきましょう: + <ul> + <li>The first argument specifies the texture target; setting this to <var>GL_TEXTURE_2D</var> means this operation will generate a texture on the currently bound texture object at the same target (so any textures bound to targets <var>GL_TEXTURE_1D</var> or <var>GL_TEXTURE_3D</var> will not be affected).</li> + <li>1つ目の引数はテクスチャのターゲットを指定します。この引数を<var>GL_TEXTURE_2D</var>にすることで、現在紐付いているテクスチャオブジェクトのうちこのターゲットのものに対してテクスチャを作成できます(現在紐付いている<var>GL_TEXTURE_1D</var>や<var>GL_TEXTURE_3D</var>には影響しません)。</li> + <li>The second argument specifies the mipmap level for which we want to create a texture for if you want to set each mipmap level manually, but we'll leave it at the base level which is <code>0</code>.</li> + <li>2つ目の引数ではテクスチャを作成する際のミップマップレベルを手動で設定したい場合にそのレベルを指定します。しかし今回は既定値の<code>0</code>にしておきましょう。</li> + <li>The third argument tells OpenGL in what kind of format we want to store the texture. Our image has only <code>RGB</code> values so we'll store the texture with <code>RGB</code> values as well.</li> + <li>3つ目はテクスチャを保存する形式を指定します。今回利用する画像は<code>RGB</code>の値のみを含むので、保存形式も<code>RGB</code>にしておきます。</li> + <li>The 4th and 5th argument sets the width and height of the resulting texture. We stored those earlier when loading the image so we'll use the corresponding variables.</li> + <li>4つ目と5つ目は出力されるテクスチャの幅と高さをそれぞれ指定します。先程画像を読み込む際にこれらの値を取得していたのでそのまま利用します。</li> + <li>The next argument should always be <code>0</code> (some legacy stuff).</li> + <li>次の引数は常に<code>0</code>にしておくべきです(過去の遺物です)。</li> + <li>The 7th and 8th argument specify the format and datatype of the source image. We loaded the image with <code>RGB</code> values and stored them as <code>char</code>s (bytes) so we'll pass in the corresponding values.</li> + <li>7つ目と8つ目では読み込む画像のデータ形式を指定します。<code>RGB</code>値の画像を読み込み、<code>char</code>形式(バイト形式)で保存したので、それらの形式をそれぞれ渡します。</li> + <li>The last argument is the actual image data.</li> + <li>最後の引数は実際の画像データです。</li> + </ul> +</p> + +<p> + Once <fun><function id='52'>glTexImage2D</function></fun> is called, the currently bound texture object now has the texture image attached to it. However, currently it only has the base-level of the texture image loaded and if we want to use mipmaps we have to specify all the different images manually (by continually incrementing the second argument) or, we could call <fun><function id='51'>glGenerateMipmap</function></fun> after generating the texture. This will automatically generate all the required mipmaps for the currently bound texture. +ひとたび<fun><function id='52'>glTexImage2D</function></fun>が呼ばれると、現在紐付いているテクスチャオブジェクトにテクスチャ画像が貼り付きます。しかしこれだけではテクスチャ画像の元のサイズのものが読み込まれただけなので、ミップマップを利用しようと思うといちいち別の画像を手動で(2つ目の引数を連続的に増加させることにより)指定しないといけません。しかし、別の方法として、テクスチャを生成した後<fun><function id='51'>glGenerateMipmap</function></fun>を呼ぶこともできます。これにより現在紐付いているテクスチャに必要なミップマップをすべて自動で作成されます。 +</p> + +<p> + After we're done generating the texture and its corresponding mipmaps, it is good practice to free the image memory: + テクスチャとミップマップを作成した後は画像のメモリを開放するのがいいでしょう: +</p> + +<pre class="cpp"><code> +stbi_image_free(data); +</code></pre> + +<p> + The whole process of generating a texture thus looks something like this: + 以上をまとめると、テクスチャの作成手順は以下のようになります: +</p> + +<pre><code> +unsigned int texture; +<function id='50'>glGenTextures</function>(1, &amp;texture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); +// set the texture wrapping/filtering options (on the currently bound texture object) +// テクスチャの繰り返しとフィルタリングの設定(現在紐付いているテクスチャオブジェクトに対して) +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +// load and generate the texture +// テクスチャの読込みと作成 +int width, height, nrChannels; +unsigned char *data = stbi_load("container.jpg", &amp;width, &amp;height, &amp;nrChannels, 0); +if (data) +{ + <function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + <function id='51'>glGenerateMipmap</function>(GL_TEXTURE_2D); +} +else +{ + std::cout &lt;&lt; "Failed to load texture" &lt;&lt; std::endl; +} +stbi_image_free(data); +</code></pre> + +<h2>Applying textures</h2> +<h2>テクスチャの適応</h2> +<p> + For the upcoming sections we will use the rectangle shape drawn with <fun><function id='2'>glDrawElements</function></fun> from the final part of the <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">Hello Triangle</a> chapter. + We need to inform OpenGL how to sample the texture so we'll have to update the vertex data with the texture coordinates: + 以降の章では<a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">はじめての三角形</a>の最後において<fun><function id='2'>glDrawElements</function></fun>を用いて描画した四角形を利用します。テクスチャのサンプリング方法をOpenGLに伝える必要があるので、頂点データにテクスチャ座標を追加します: +</p> + +<pre><code> +float vertices[] = { + // positions // colors // texture coords + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left +}; +</code></pre> + +<p> + Since we've added an extra vertex attribute we again have to notify OpenGL of the new vertex format: + 頂点属性を追加したのでOpenGLに新しい頂点のフォーマットを伝えます: +</p> + +<img src="/img/getting-started/vertex_attribute_pointer_interleaved_textures.png" class="clean" alt="Image of VBO with interleaved position, color and texture data with strides and offsets shown for configuring vertex attribute pointers."/> + +<pre><code> +<function id='30'>glVertexAttribPointer</function>(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(2); +</code></pre> + +<p> + Note that we have to adjust the stride parameter of the previous two vertex attributes to <code>8 * sizeof(float)</code> as well. + ほかの頂点属性に対するストライドの値を<code>8 * sizeof(float)</code>に変更する必要があることに注意してください。 +</p> + +<p> + Next we need to alter the vertex shader to accept the texture coordinates as a vertex attribute and then forward the coordinates to the fragment shader: + 続いて頂点シェーダーを変更して、テクスチャ座標を頂点属性として受け取りフラグメントシェーダーに取り次ぐようにします: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; +layout (location = 2) in vec2 aTexCoord; + +out vec3 ourColor; +out vec2 TexCoord; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + ourColor = aColor; + TexCoord = aTexCoord; +} +</code></pre> + +<p> + The fragment shader should then accept the <code>TexCoord</code> output variable as an input variable. + これで<code>TexCoord</code>という出力をフラグメントシェーダーが入力として受け取れるようになりました。 +</p> + +<p> + The fragment shader should also have access to the texture object, but how do we pass the texture object to the fragment shader? GLSL has a built-in data-type for texture objects called a <def>sampler</def> that takes as a postfix the texture type we want e.g. <code>sampler1D</code>, <code>sampler3D</code> or in our case <code>sampler2D</code>. We can then add a texture to the fragment shader by simply declaring a <code>uniform sampler2D</code> that we later assign our texture to. + フラグメントシェーダーはテクスチャオブジェクトにもアクセスできるべきですが、どのようにしてテクスチャオブジェクトをフラグメントシェーダーに渡せるでしょうか。GLSLには<def>サンプラ</def>と呼ばれるテクスチャオブジェクトのデータ型が組込まれていて、語尾にテクスチャの種類を付けて利用できます。例えば<code>sampler1D</code>、<code>sampler2D</code>、<code>sampler3D</code>といったもので、今回は<code>sampler2D</code>を利用します。<code>uniform sampler2D</code>を宣言するだけで、フラグメントシェーダーにテクスチャを追加することができます。その後このユニフォームにテクスチャを割り当てます。 +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec3 ourColor; +in vec2 TexCoord; + +uniform sampler2D ourTexture; + +void main() +{ + FragColor = texture(ourTexture, TexCoord); +} +</code></pre> + +<p> + To sample the color of a texture we use GLSL's built-in <fun>texture</fun> function that takes as its first argument a texture sampler and as its second argument the corresponding texture coordinates. The <fun>texture</fun> function then samples the corresponding color value using the texture parameters we set earlier. The output of this fragment shader is then the (filtered) color of the texture at the (interpolated) texture coordinate. + テクスチャの色をサンプリングするには、GLSLに組込まれている<fun>texture</fun>関数を利用します。1つ目の引数はテクスチャのサンプラで、2つ目は対応するテクスチャ座標です。<fun>texture</fun>関数は先程設定したテクスチャの情報を用いて色をサンプリングします。これでフラグメントシェーダーの出力はテクスチャ座標の(補完された)各点におけるテクスチャの(フィルタリングされた)色になります。 +</p> + +<p> + All that's left to do now is to bind the texture before calling <fun><function id='2'>glDrawElements</function></fun> and it will then automatically assign the texture to the fragment shader's sampler: + 最後にやり残したことは<fun><function id='2'>glDrawElements</function></fun>を呼ぶ前にテクスチャを紐付けることです。これによりフラグメントシェーダーのサンプラにテクスチャが自動的に割り当てられます: +</p> + +<pre class="cpp"><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); +<function id='27'>glBindVertexArray</function>(VAO); +<function id='2'>glDrawElements</function>(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +</code></pre> + +<p> + If you did everything right you should see the following image: + すべて間違わずにできていれば以下のような画像が出力されるはずです: +</p> + +<img src="/img/getting-started/textures2.png" class="clean"/> + +<p> + If your rectangle is completely white or black you probably made an error along the way. Check your shader logs and try to compare your code with the application's <a href="/code_viewer_gh.php?code=src/1.getting_started/4.1.textures/textures.cpp" target="_blank">source code</a>. + もし四角形が真っ白や真っ黒に表示されたら、どこかに手違いがあるはずです。シェーダーのログを確認し、自分のコードをアプリケーションの<a href="/code_viewer_gh.php?code=src/1.getting_started/4.1.textures/textures.cpp" target="_blank">ソースコード</a>と見比べて下さい。 +</p> + +<warning> + If your texture code doesn't work or shows up as completely black, continue reading and work your way to the last example that <strong>should</strong> work. On some drivers it is <strong>required</strong> to assign a texture unit to each sampler uniform, which is something we'll discuss further in this chapter. + もしテクスチャのコードが動かなかったり真っ黒なものを表示した場合、とりあえず最後の例まで読み進めて下さい。最後の例は機能する<strong>はず</strong>です。ドライバによってはテクスチャユニットを各々のサンプラユニフォームに割り当てることが<strong>必要</strong>です。テクスチャユニットについてはこの章で詳しく議論します。 +</warning> + +<p> + To get a little funky we can also mix the resulting texture color with the vertex colors. We simply multiply the resulting texture color with the vertex color in the fragment shader to mix both colors: + 少しイカしたことをするために出力されたテクスチャの色を頂点の色と混ぜてみましょう。この為にフラグメントシェーダーにおいてテクスチャの色と頂点の色を単に掛け合わせます: +</p> + +<pre><code> +FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0); +</code></pre> + +<p> + The result should be a mixture of the vertex's color and the texture's color: + その結果、頂点の色とテクスチャの色が混ぜ合わせられます: +</p> + +<img src="/img/getting-started/textures_funky.png" class="clean"/> + +<p> + I guess you could say our container likes to disco. + コンテナがディスコのように見えるでしょう。<!--平成生まれの役者にはなんのことやら。--> +</p> + +<h2>Texture Units</h2> +<h2>テクスチャユニット</h2> +<p> + You probably wondered why the <code>sampler2D</code> variable is a uniform if we didn't even assign it some value with <fun><function id='44'>glUniform</function></fun>. Using <fun><function id='44'>glUniform</function>1i</fun> we can actually assign a <em>location</em> value to the texture sampler so we can set multiple textures at once in a fragment shader. This location of a texture is more commonly known as a <def>texture unit</def>. The default texture unit for a texture is <code>0</code> which is the default active texture unit so we didn't need to assign a location in the previous section; note that not all graphics drivers assign a default texture unit so the previous section may not have rendered for you. + <fun><function id='44'>glUniform</function></fun>によって値を設定しない場合であっても<code>sampler2D</code>がユニフォームであることについて疑問に思うかもしれません。<fun><function id='44'>glUniform</function>1i</fun>を利用することで、テクスチャサンプラに<em>位置</em>を実際に割り当てられるので、フラグメントシェーダーにおいて複数のテクスチャを一度に指定することができます。このテクスチャの位置は一般に<def>テクスチャユニット</def>と呼ばれます。テクスチャユニットの既定値は<code>0</code>で、これがデフォルトのアクティブなテクスチャユニットなので、前の章において位置を割り当てる必要はなかったのです。ただし全てのグラフィックドライバがデフォルトのテクスチャユニットを割り当てるわけではないので、場合によっては前章のコードでは描画されないことがあるのです。 +</p> + +<p> + The main purpose of texture units is to allow us to use more than 1 texture in our shaders. By assigning texture units to the samplers, we can bind to multiple textures at once as long as we activate the corresponding texture unit first. Just like <fun><function id='48'>glBindTexture</function></fun> we can activate texture units using <fun><function id='49'>glActiveTexture</function></fun> passing in the texture unit we'd like to use: + テクスチャユニットの主な目的はシェーダーにおいて複数のテクスチャを利用することです。サンプラにテクスチャユニットを割り当て、呼応するテクスチャユニットを最初にアクティベートしておけば、複数のテクスチャを一度に紐付けることができます。<fun><function id='48'>glBindTexture</function></fun>と同様に、<fun><function id='49'>glActiveTexture</function></fun>に、利用したいテクスチャユニットを渡すことで、それをアクティベートすることができます: +</p> + +<pre class="cpp"><code> +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); // activate the texture unit first before binding texture +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); // テクスチャを紐付ける前にまずテクスチャユニットをアクティベート +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); +</code></pre> + +<p> + After activating a texture unit, a subsequent <fun><function id='48'>glBindTexture</function></fun> call will bind that texture to the currently active texture unit. Texture unit <var>GL_TEXTURE0</var> is always by default activated, so we didn't have to activate any texture units in the previous example when using <fun><function id='48'>glBindTexture</function></fun>. + テクスチャユニットをアクティベートした後で<fun><function id='48'>glBindTexture</function></fun>を呼び出すとそのテクスチャが現在アクティブなテクスチャユニットに紐付きます。<var>GL_TEXTURE0</var>というテクスチャユニットはデフォルトで常にアクティブな状態なので、前の例において、<fun><function id='48'>glBindTexture</function></fun>を利用した際にテクスチャユニットをアクティベートする必要がなかったのです。 +</p> + +<note> + OpenGL should have a at least a minimum of 16 texture units for you to use which you can activate using <var>GL_TEXTURE0</var> to <var>GL_TEXTURE15</var>. They are defined in order so we could also get <var>GL_TEXTURE8</var> via <var>GL_TEXTURE0 + 8</var> for example, which is useful when we'd have to loop over several texture units. + OpenGLでは少なくとも16個のテクスチャユニットを利用でき、<var>GL_TEXTURE0</var>から<var>GL_TEXTURE15</var>を使ってアクティベートできます。これらのテクスチャユニットは例えば<var>GL_TEXTURE8</var>に<var>GL_TEXTURE0 + 8</var>という形でアクセスできるように定義されており、テクスチャユニットに対してループ処理を行う際に便利です。 +</note> + +<p> + We still however need to edit the fragment shader to accept another sampler. This should be relatively straightforward now: + しかしまだフラグメントシェーダーが他のサンプラを受け入れるように変更を加える必要があります。これは比較的簡単です: +</p> + +<pre><code> +#version 330 core +... + +uniform sampler2D texture1; +uniform sampler2D texture2; + +void main() +{ + FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2); +} +</code></pre> + +<p> + The final output color is now the combination of two texture lookups. GLSL's built-in <fun>mix</fun> function takes two values as input and linearly interpolates between them based on its third argument. If the third value is <code>0.0</code> it returns the first input; if it's <code>1.0</code> it returns the second input value. A value of <code>0.2</code> will return <code>80%</code> of the first input color and <code>20%</code> of the second input color, resulting in a mixture of both our textures. + 最終的に出力される色がふたつのテクスチャの組み合わせになりました。GLSLの組み込み関数である<fun>mix</fun>は2つの入力値を受け取り、3つ目の引数に基づいてそれらの値を線形補完します。3つ目の値が<code>0.0</code>であれば1つ目の入力がそのまま返され、<code>1.0</code>であれば2つ目の入力が返ります。<code>0.2</code>であれば1つ目の入力の80%と2つ目の入力の20%が返り、結果としてふたつのテクスチャを混ぜたものになります。 +</p> + +<p> + We now want to load and create another texture; you should be familiar with the steps now. Make sure to create another texture object, load the image and generate the final texture using <fun><function id='52'>glTexImage2D</function></fun>. For the second texture we'll use an image of your <a href="/img/textures/awesomeface.png" target="_blank">facial expression while learning OpenGL</a>: + 今度はもうひとつのテクスチャを読み込んで作成しましょう。手順はすでにご存知のはずです。テクスチャオブジェクトをもうひとつ作成し、画像を読込み<fun><function id='52'>glTexImage2D</function></fun>により最終的なテクスチャを生成します。ふたつ目のテクスチャには<a href="/img/textures/awesomeface.png" target="_blank">OpenGLを学習中の表情</a>の画像を利用しましょう: +</p> + +<pre><code> +unsigned char *data = stbi_load("awesomeface.png", &amp;width, &amp;height, &amp;nrChannels, 0); +if (data) +{ + <function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + <function id='51'>glGenerateMipmap</function>(GL_TEXTURE_2D); +} +</code></pre> + +<p> + Note that we now load a <code>.png</code> image that includes an alpha (transparency) channel. This means we now need to specify that the image data contains an alpha channel as well by using <var>GL_RGBA</var>; otherwise OpenGL will incorrectly interpret the image data. + 今回読み込んだのは<code>.png</code>画像であり、アルファ(透明)チャネルを含むことに注意してください。今回は画像データにアルファチャネルが含まれていることも<var>GL_RGBA</var>を用いて示さなければなりません。そうしないとOenGLが正しく画像データを解釈してくれません。 +</p> + +<p> + To use the second texture (and the first texture) we'd have to change the rendering procedure a bit by binding both textures to the corresponding texture unit: + ひとつ目のテクスチャに加えふたつ目のものを利用するには両方のテクスチャを対応するテクスチャユニットに紐付けるように描画処理を少し変更する必要があります: +</p> + +<pre><code> +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture1); +<function id='49'>glActiveTexture</function>(GL_TEXTURE1); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture2); + +<function id='27'>glBindVertexArray</function>(VAO); +<function id='2'>glDrawElements</function>(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +</code></pre> + +<p> + We also have to tell OpenGL to which texture unit each shader sampler belongs to by setting each sampler using <fun><function id='44'>glUniform</function>1i</fun>. We only have to set this once, so we can do this before we enter the render loop: + また各シェーダーサンプラを<fun><function id='44'>glUniform</function>1i</fun>により設定することで、そのサンプラがどのテクスチャユニットに属するかをOpenGLに伝える必要があります。これは一度だけでいいので、描画ループに入る前の段階で実行します: +</p> + +<pre><code> +ourShader.use(); // don't forget to activate the shader before setting uniforms! +ourShader.use(); // ユニフォームを設定する前にシェーダーをアクティベートする +<function id='44'>glUniform</function>1i(<function id='45'>glGetUniformLocation</function>(ourShader.ID, "texture1"), 0); // set it manually +<function id='44'>glUniform</function>1i(<function id='45'>glGetUniformLocation</function>(ourShader.ID, "texture1"), 0); // 手動で設定 +ourShader.setInt("texture2", 1); // or with shader class +ourShader.setInt("texture2", 1); // またはシェーダークラスを利用 + +while(...) +{ + [...] +} +</code></pre> + +<p> + By setting the samplers via <fun><function id='44'>glUniform</function>1i</fun> we make sure each uniform sampler corresponds to the proper texture unit. You should get the following result: +<fun><function id='44'>glUniform</function>1i</fun>によりサンプラを設定することで、各ユニフォームサンプラが適切なテクスチャユニットに確実に対応させます。以下の結果が得られるでしょう: +</p> + +<img src="/img/getting-started/textures_combined.png" class="clean"/> + +<p> + You probably noticed that the texture is flipped upside-down! This happens because OpenGL expects the <code>0.0</code> coordinate on the y-axis to be on the bottom side of the image, but images usually have <code>0.0</code> at the top of the y-axis. Luckily for us, <code>stb_image.h</code> can flip the y-axis during image loading by adding the following statement before loading any image: + テクスチャが上下さかさになっていますね。これはOpenGLにおいてy軸上の<code>0.0</code>が画像の下端に対応しているからです。しかし普通y軸の<code>0.0</code>は上端です。有り難いことに<code>stb_image.h</code>は画像読込み前に以下の宣言をすることで読み込む画像を上下反転してくれます: + </p> + +<pre><code> +stbi_set_flip_vertically_on_load(true); +</code></pre> + +<p> + After telling <code>stb_image.h</code> to flip the y-axis when loading images you should get the following result: + 読み込み時にy軸に沿って画像を反転するように<code>stb_image.h</code>に伝えれば、以下のような結果が得られるでしょう: +</p> + +<img src="/img/getting-started/textures_combined2.png" class="clean"/> + +<p> + If you see one happy container, you did things right. You can compare it with the <a href="/code_viewer_gh.php?code=src/1.getting_started/4.2.textures_combined/textures_combined.cpp" target="_blank">source code</a>. + 幸せそうな箱が現れれば完璧です。<a href="/code_viewer_gh.php?code=src/1.getting_started/4.2.textures_combined/textures_combined.cpp" target="_blank">ソースコード</a>と比較してみて下さい。 +</p> + +<h2>Exercises</h2> +<h2>演習問題</h2> +<p> + To get more comfortable with textures it is advised to work through these exercises before continuing. + 次の章に進む前に、以下の演習問題を解いてテクスチャに慣れておくのがいいでしょう。 + <ul> + <li>Make sure <strong>only</strong> the happy face looks in the other/reverse direction by changing the fragment shader: <a href="/code_viewer_gh.php?code=src/1.getting_started/4.3.textures_exercise1/textures_exercise1.cpp" target="_blank">solution</a>.</li> + <li>フラグメントシェーダーを変更して笑顔<strong>だけ</strong>が別の方向に向くようにしてください: <a href="/code_viewer_gh.php?code=src/1.getting_started/4.3.textures_exercise1/textures_exercise1.cpp" target="_blank">解答</a>。</li> + <li>Experiment with the different texture wrapping methods by specifying texture coordinates in the range <code>0.0f</code> to <code>2.0f</code> instead of <code>0.0f</code> to <code>1.0f</code>. See if you can display 4 smiley faces on a single container image clamped at its edge: <a href="/code_viewer_gh.php?code=src/1.getting_started/4.4.textures_exercise2/textures_exercise2.cpp" target="_blank">solution</a>, <a href="/img/getting-started/textures_exercise2.png" target="_blank">result</a>. See if you can experiment with other wrapping methods as well.</li> + <li>テクスチャ座標を<code>0.0f</code>から<code>1.0f</code>ではなく<code>0.0</code>から<code>2.0f</code>に変更したうえで、テクスチャの繰り返し方法を別のものにして結果がどうなるか実験してください。箱の画像が端で引き伸ばされ、その上に笑顔が4つ表示されているようなものを作成できるでしょうか:<a href="/code_viewer_gh.php?code=src/1.getting_started/4.4.textures_exercise2/textures_exercise2.cpp" target="_blank">解答</a>、<a href="/img/getting-started/textures_exercise2.png" target="_blank">結果</a>。他の繰り返し方法でも試して下さい。</li> + <li>Try to display only the center pixels of the texture image on the rectangle in such a way that the individual pixels are getting visible by changing the texture coordinates. Try to set the texture filtering method to <var>GL_NEAREST</var> to see the pixels more clearly: <a href="/code_viewer_gh.php?code=src/1.getting_started/4.5.textures_exercise3/textures_exercise3.cpp" target="_blank">solution</a>.</li> + <li>ピクセルが認識できるようにテクスチャ座標を変更することで四角形の上にテクスチャ画像の中心のピクセルだけを表示させて下さい。さらにピクセルがくっきり見えるようにフィルタリング方法を<var>GL_NEAREST</var>にしてみてください:<a href="/code_viewer_gh.php?code=src/1.getting_started/4.5.textures_exercise3/textures_exercise3.cpp" target="_blank">解答</a>。</li> + <li>Use a uniform variable as the <fun>mix</fun> function's third parameter to vary the amount the two textures are visible. Use the up and down arrow keys to change how much the container or the smiley face is visible: <a href="/code_viewer_gh.php?code=src/1.getting_started/4.6.textures_exercise4/textures_exercise4.cpp" target="_blank">solution</a>.</li> + </li>ふたつのテクスチャの見え方を変化させられるよう、<fun>mix</fun>関数の3つ目の引数にユニフォームを渡して下さい。上下の矢印キーにより箱と笑顔の見え方を変更できるようにしてください:<a href="/code_viewer_gh.php?code=src/1.getting_started/4.6.textures_exercise4/textures_exercise4.cpp" target="_blank">解答</a>。</li> + </ul> +</p> + + + </div> + +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Getting-started/Transformations.html b/pub/Getting-started/Transformations.html @@ -0,0 +1,1018 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Transformations</h1> + <h1 id="content-title">変換</h1> +<h1 id="content-url" style='display:none;'>Getting-started/Transformations</h1> +<p> + We now know how to create objects, color them and/or give them a detailed appearance using textures, but they're still not that interesting since they're all static objects. We could try and make them move by changing their vertices and re-configuring their buffers each frame, but that's cumbersome and costs quite some processing power. There are much better ways to <def>transform</def> an object and that's by using (multiple) <def>matrix</def> objects. This doesn't mean we're going to talk about Kung Fu and a large digital artificial world. + これまでオブジェクトを作成し、色を着け、あるいはテクスチャにより精細な見た目を与えることを学びました。しかしここまで作成した物体は静止したままで、あまり面白くありません。フレーム毎に頂点の座標を動かしバッファを再設定することで物体を動かすこともできましょうが、これは面倒であるうえに処理にコストがかかります。よりよい方法として、いくつかの行列を用いて物体を<def>変換</def>するというものがあります。ただし美味しいラーメン屋の話をするわけではありません。 +</p> + +<p> + Matrices are very powerful mathematical constructs that seem scary at first, but once you'll grow accustomed to them they'll prove extremely useful. When discussing matrices, we'll have to make a small dive into some mathematics and for the more mathematically inclined readers I'll post additional resources for further reading. + 行列とはとても便利な数学の道具です。はじめは得体の知れないものかもしれませんが、一度慣れてしまえばその便利さは手放せません。行列の話をする上で、少しばかり数学に足を踏み入れなければなりません。また数学が好きな人の為にさらに発展的な内容の文献も挙げておきます。 +</p> + +<p> + However, to fully understand transformations we first have to delve a bit deeper into vectors before discussing matrices. The focus of this chapter is to give you a basic mathematical background in topics we will require later on. If the subjects are difficult, try to understand them as much as you can and come back to this chapter later to review the concepts whenever you need them. + しかし変換について完全に理解するには行列の話をする前にもう少しベクトルについて深掘りする必要があります。この章では後程必要となる基礎的な数学的の知識を身に付けていただきます。もし難しく感じた場合、可能な限り理解しようとしたうえで、後から必要な概念についてこの章を振り返るようにしてください。 +</p> + +<h1>Vectors</h1> +<h1>ベクトル</h1> +<p> + In its most basic definition, vectors are directions and nothing more. A vector has a <def>direction</def> and a <def>magnitude</def> (also known as its strength or length). You can think of vectors like directions on a treasure map: 'go left 10 steps, now go north 3 steps and go right 5 steps'; here 'left' is the direction and '10 steps' is the magnitude of the vector. The directions for the treasure map thus contains 3 vectors. Vectors can have any dimension, but we usually work with dimensions of 2 to 4. If a vector has 2 dimensions it represents a direction on a plane (think of 2D graphs) and when it has 3 dimensions it can represent any direction in a 3D world. + 最も基本的な定義において、ベクトルとは矢印以外のなにものでもありません。ベクトルは<def>方向</def>と<def>大きさ</def>(あるいは強さや長さとも)を持ちます。ベクトルは宝の地図に書かれた指示のように捉えることができます: 「左に10歩進み、北に3歩、その後右に5歩進む」この場合「左」がベクトルの向きで「10歩」がベクトルの大きさです。つまりこの宝の地図には3つのベクトルがあります。ベクトルは任意の次元を持つことができますが、この本で利用するのはだいたい2次元から4次元までです。ベクトルが2次元であればそれは平面上の方向を表し(2次元の座標空間を思いうかべて下さい)、3次元であれば3次元空間での任意の方向を表わします。 +</p> + +<p> + Below you'll see 3 vectors where each vector is represented with <code>(x,y)</code> as arrows in a 2D graph. Because it is more intuitive to display vectors in 2D (rather than 3D) you can think of the 2D vectors as 3D vectors with a <code>z</code> coordinate of <code>0</code>. Since vectors represent directions, the origin of the vector does not change its value. In the graph below we can see that the vectors \({\color{red}\bar{v}}\) and \({\color{blue}\bar{w}}\) are equal even though their origin is different: + 下の図は2次元の座標空間上に<code>(x, y)</code>と共に矢印で図示した3つのベクトルです。ベクトルは3次元より2次元で図示する方が直感的なので、2次元ベクトルは<code>z</code>座標が<code>0</code>である3次元ベクトルだと見做せます。ベクトルは方向を表わしたものなので、始点が変化してもベクトル自身は同じものです。下の図において2つのベクトル\({\color{red}\bar{v}}\)と\({\color{blue}\bar{w}}\)は始点は違いますがベクトルとしては同じものです: +</p> + +<img src="/img/getting-started/vectors.png" class="clean" /> + +<p> + When describing vectors mathematicians generally prefer to describe vectors as character symbols with a little bar over their head like \(\bar{v}\). Also, when displaying vectors in formulas they are generally displayed as follows: + 数学者はよく\(\bar{v}\)のように文字の上に小さな棒を書いてベクトルを表わす書き方をします。そして数式の中にベクトルを書く際は一般に以下のような形になります: + + \[\bar{v} = \begin{pmatrix} {\color{red}x} \\ {\color{green}y} \\ {\color{blue}z} \end{pmatrix} \] +</p> + +<p> + Because vectors are specified as directions it is sometimes hard to visualize them as positions. If we want to visualize vectors as positions we can imagine the origin of the direction vector to be <code>(0,0,0)</code> and then point towards a certain direction that specifies the point, making it a <def>position vector</def> (we could also specify a different origin and then say: 'this vector points to that point in space from this origin'). The position vector <code>(3,5)</code> would then point to <code>(3,5)</code> on the graph with an origin of <code>(0,0)</code>. Using vectors we can thus describe directions <strong>and</strong> positions in 2D and 3D space. + ベクトルは方向を表わすので、位置として可視化するのは困難です。ベクトルを位置として捉えるには<vode>(0, 0, 0)</code>をベクトルの始点としたときにそのベクトルが差す点をそのベクトルの位置とします。この見方でのベクトルを<def>位置ベクトル</def>といいます(他のある点を始点として見た場合、「このベクトルは空間上でその点を始点として見たときにあそこの点を差す」というような言い方をします)。<code>(3, 5)</code>という位置ベクトルは<code>(0, 0)</code>を始点と見たときに<code>(3, 5)</code>を指し示すようなベクトルです。ベクトルを利用することで、2次元空間や3次元空間での方向<strong>及び</strong>位置を表わせるようになりました。 +</p> + +<p> + Just like with normal numbers we can also define several operations on vectors (some of which you've already seen). + 通常の数字と同様にベクトルに関してもいくつかの演算を定義できます(既に少しでてきました)。 +</p> + +<h2>Scalar vector operations</h2> +<h2>スカラとベクトルの演算</h2> +<p> + A <def>scalar</def> is a single digit. When adding/subtracting/multiplying or dividing a vector with a scalar we simply add/subtract/multiply or divide each element of the vector by the scalar. For addition it would look like this: + <def>スカラ</def>というのは数字です。ベクトルに対してスカラを足す/引く/掛ける演算は単にベクトルの各要素に対してスカラを足す/引く/掛けるだけです。足し算は以下のようになります: + + \[ \begin{pmatrix} {\color{red}1} \\ {\color{green}2} \\ {\color{blue}3} \end{pmatrix} + x \rightarrow \begin{pmatrix} {\color{red}1} \\ {\color{green}2} \\ {\color{blue}3} \end{pmatrix} + \begin{pmatrix} x \\ x \\ x \end{pmatrix} = \begin{pmatrix} {\color{red}1} + x \\ {\color{green}2} + x \\ {\color{blue}3} + x \end{pmatrix} \] + + Where \(+\) can be \(+\),\(-\),\(\cdot\) or \(\div\) where \(\cdot\) is the multiplication operator. + \(+\)になっているところは\(+\)、\(-\)、\(\cdot\)あるいは\(\div\)が使えます。ただし\(\cdot\)は掛け算です。 +</p> + +<h2>Vector negation</h2> +<h2>ベクトルの反転</h2> +<p> + Negating a vector results in a vector in the reversed direction. A vector pointing north-east would point south-west after negation. To negate a vector we add a minus-sign to each component (you can also represent it as a scalar-vector multiplication with a scalar value of <code>-1</code>): + ベクトルを反転させるというのはベクトルの向きを逆にするということです。北東を向いていたベクトルは反転により南西を向くようになります。ベクトルを反転させるには各要素にマイナスの記号を付ければよいです(あるいはベクトルに対して<code>-1</code>を掛けることでも反転できます): + + \[-\bar{v} = -\begin{pmatrix} {\color{red}v_x} \\ {\color{blue}v_y} \\ {\color{green}v_z} \end{pmatrix} = \begin{pmatrix} -{\color{red}v_x} \\ -{\color{blue}v_y} \\ -{\color{green}v_z} \end{pmatrix} \] +</p> + +<h2>Addition and subtraction</h2> +<h2>加法、減法</h2> +<p> + Addition of two vectors is defined as <def>component-wise</def> addition, that is each component of one vector is added to the same component of the other vector like so: + 2つのベクトルの足し算は<def>要素毎</def>の足し算として定義されます。つまり以下のように、一方のベクトルの各要素が他方のベクトルの同じ要素と足し合せられるということです: + + \[\bar{v} = \begin{pmatrix} {\color{red}1} \\ {\color{green}2} \\ {\color{blue}3} \end{pmatrix}, \bar{k} = \begin{pmatrix} {\color{red}4} \\ {\color{green}5} \\ {\color{blue}6} \end{pmatrix} \rightarrow \bar{v} + \bar{k} = \begin{pmatrix} {\color{red}1} + {\color{red}4} \\ {\color{green}2} + {\color{green}5} \\ {\color{blue}3} + {\color{blue}6} \end{pmatrix} = \begin{pmatrix} {\color{red}5} \\ {\color{green}7} \\ {\color{blue}9} \end{pmatrix} \] + + Visually, it looks like this on vectors <code>v=(4,2)</code> and <code>k=(1,2)</code>, where the second vector is added on top of the first vector's end to find the end point of the resulting vector (head-to-tail method): + 視覚的には以下の図のように、2つのベクトル<code>v=(4, 2)</code>と<code>k=(1, 2)</code>を足す場合、2つ目のベクトルが1つ目のベクトルの終点から始まり、結果として足し合せられたベクトルは1つ目のベクトルの始点から2つ目のベクトルの終点を差すようになります: +</p> + + <img src="/img/getting-started/vectors_addition.png" class="clean"/> + +<p> + Just like normal addition and subtraction, vector subtraction is the same as addition with a negated second vector: + 通常の加法、減法と同様に、ベクトルの引き算は2つ目のベクトルを反転させたものを足し合せるだけです: + + \[\bar{v} = \begin{pmatrix} {\color{red}1} \\ {\color{green}2} \\ {\color{blue}3} \end{pmatrix}, \bar{k} = \begin{pmatrix} {\color{red}4} \\ {\color{green}5} \\ {\color{blue}6} \end{pmatrix} \rightarrow \bar{v} + -\bar{k} = \begin{pmatrix} {\color{red}1} + (-{\color{red}4}) \\ {\color{green}2} + (-{\color{green}5}) \\ {\color{blue}3} + (-{\color{blue}6}) \end{pmatrix} = \begin{pmatrix} -{\color{red}3} \\ -{\color{green}3} \\ -{\color{blue}3} \end{pmatrix} \] + +</p> + +<p> + Subtracting two vectors from each other results in a vector that's the difference of the positions both vectors are pointing at. This proves useful in certain cases where we need to retrieve a vector that's the difference between two points. + 2つのベクトルの引き算の結果は、双方が差す位置の差分となります。この性質は2つのベクトルの指し示す位置の違いを得る必要がある場合に便利です。 +</p> + +<img src="/img/getting-started/vectors_subtraction.png" class="clean"/> + + +<h2>Length</h2> +<h2>長さ</h2> +<p> + To retrieve the length/magnitude of a vector we use the <def>Pythagoras theorem</def> that you may remember from your math classes. A vector forms a triangle when you visualize its individual <code>x</code> and <code>y</code> component as two sides of a triangle: + ベクトルの長さを得るには数学の授業で習った<def>ピタゴラスの定理</def>を利用します。ベクトルの<code>x</code>と<code>y</code>の要素をそれぞれ三角形の辺と見ることで、ベクトルが三角形を形成します: +</p> + +<img src="/img/getting-started/vectors_triangle.png" class="clean"/> + +<p> + Since the length of the two sides <code>(x, y)</code> are known and we want to know the length of the tilted side \({\color{red}\bar{v}}\) we can calculate it using the Pythagoras theorem as: + 2辺の長さ<code>(x, y)</code>は既知なので、斜辺の長さはピタゴラスの定理より: + + \[||{\color{red}\bar{v}}|| = \sqrt{{\color{green}x}^2 + {\color{blue}y}^2} \] + + Where \(||{\color{red}\bar{v}}||\) is denoted as <em>the length of vector \({\color{red}\bar{v}}\)</em>. This is easily extended to 3D by adding \(z^2\) to the equation. + となります。ここで\(||{\color{red}\bar{v}}||\)は<em>ベクトル\({\color{red}\bar{v}}\)の長さ</em>を表わします。ここに\(z^2\)を加えることで簡単に3次元に拡張できます。 +</p> + +<p> + In this case the length of vector <code>(4, 2)</code> equals: + これを用いると、ベクトル<code>(4, 2)</code>の長さは: + + \[||{\color{red}\bar{v}}|| = \sqrt{{\color{green}4}^2 + {\color{blue}2}^2} = \sqrt{{\color{green}16} + {\color{blue}4}} = \sqrt{20} = 4.47 \] + + Which is <code>4.47</code>. + となり、これは<code>4.47</code>です。 +</p> + + + +<p> + There is also a special type of vector that we call a <def>unit vector</def>. A unit vector has one extra property and that is that its length is exactly 1. We can calculate a unit vector \(\hat{n}\) from any vector by dividing each of the vector's components by its length: + また、<def>単位ベクトル</def>と呼ばれる特別なベクトルもあります。単位ベクトルとはその長さがちょうど1であるベクトルです。単位ベクトル\(\hat{n}\)は任意のベクトルから、各要素をその長さで割ることで得られます: + + \[\hat{n} = \frac{\bar{v}}{||\bar{v}||}\] + + We call this <def>normalizing</def> a vector. Unit vectors are displayed with a little roof over their head and are generally easier to work with, especially when we only care about their directions (the direction does not change if we change a vector's length). + この操作はベクトルの<def>正規化と呼ばれます。単位ベクトルは記号の上に屋根を乗せて書きます。単位ベクトルは特にその方向だけが必要な場合に便利です(ベクトルの長さを変更してもその方向は不変です)。 +</p> + +<h2>Vector-vector multiplication</h2> +<h2>ベクトルどうしの積</h2> +<p> + Multiplying two vectors is a bit of a weird case. Normal multiplication isn't really defined on vectors since it has no visual meaning, but we have two specific cases that we could choose from when multiplying: one is the <def>dot product</def> denoted as \(\bar{v} \cdot \bar{k}\) and the other is the <def>cross product</def> denoted as \(\bar{v} \times \bar{k}\). + 2つのベクトルを掛け合せるのは少し特殊です。通常の積は幾何学的な意味がないので定義されませんが、他の特殊な掛け算が存在します。ひとつは<def>内積</def>と呼ばれ、\(\bar{v} \cdot \bar{k}\)と表記されるもので、いまひとつは<def>外積</def>と呼ばれ、\(\bar{v} \times \bar{k}\)と表記されるものです。 +</p> + +<h3>Dot product</h3> +<h3>内積</h3> +<p> + The dot product of two vectors is equal to the scalar product of their lengths times the cosine of the angle between them. If this sounds confusing take a look at its formula: + ベクトルの内積はそれぞれの長さのスカラ積に、2つのベクトルがなす角のコサインを掛けたものに等しいものです。文字で書いてもややこしいので以下の式を見て下さい: + + \[\bar{v} \cdot \bar{k} = ||\bar{v}|| \cdot ||\bar{k}|| \cdot \cos \theta \] + + Where the angle between them is represented as theta (\(\theta\)). Why is this interesting? Well, imagine if \(\bar{v}\) and \(\bar{k}\) are unit vectors then their length would be equal to 1. This would effectively reduce the formula to: + ここで\(theta\)は2つのベクトルのなす角です。これのなにが面白いのでしょう。もし両方のベクトルが単位ベクトルであればその長さが1に等しいので、以下の式のように単純なものになります: + + \[\hat{v} \cdot \hat{k} = 1 \cdot 1 \cdot \cos \theta = \cos \theta\] + + Now the dot product <strong>only</strong> defines the angle between both vectors. You may remember that the cosine or cos function becomes <code>0</code> when the angle is 90 degrees or <code>1</code> when the angle is 0. This allows us to easily test if the two vectors are <def>orthogonal</def> or <def>parallel</def> to each other using the dot product (orthogonal means the vectors are at a <def>right-angle</def> to each other). In case you want to know more about the <code>sin</code> or the <code>cos</code> functions I'd suggest the following <a href="https://www.khanacademy.org/math/trigonometry/basic-trigonometry/basic_trig_ratios/v/basic-trigonometry" target="_blank">Khan Academy videos</a> about basic trigonometry. + この場合、内積は2つのベクトルのなす角<strong>だけ</strong>を表しています。コサインという関数は角度が90度なら<code>0</code>で、0度なら<code>1</code>になることを思いだして下さい。この性質を用いると、内積を計算することで2つのベクトルが<def>直交</def>するかどうか、あるいは<def>平行</def>であるかどうかを簡単に判定できます。<code>sin</code>や<code>cos</code>に興味がある場合、三角関数の基礎にかんするこの動画<a href="https://www.khanacademy.org/math/trigonometry/basic-trigonometry/basic_trig_ratios/v/basic-trigonometry" target="_blank">Khan Academy videos</a>をおすすめします。 +</p> + +<note> + You can also calculate the angle between two non-unit vectors, but then you'd have to divide the lengths of both vectors from the result to be left with \(cos \theta\). + 単位ベクトルでないベクトルの内積からそれらのなす角を求めることもできますが、その場合、内積を2つのベクトルの長さで割る必要があります。 +</note> + +<p> + So how do we calculate the dot product? The dot product is a component-wise multiplication where we add the results together. It looks like this with two unit vectors (you can verify that both their lengths are exactly <code>1</code>): + ではどのようにして内積を計算するのでしょう。内積は各要素毎に積を取り、それらを足し合わせて計算できます。2つの単位ベクトルでは以下のようになります(各ベクトルの長さが<code>1</code>であることを確認してください): + + \[ \begin{pmatrix} {\color{red}0.6} \\ -{\color{green}0.8} \\ {\color{blue}0} \end{pmatrix} \cdot \begin{pmatrix} {\color{red}0} \\ {\color{green}1} \\ {\color{blue}0} \end{pmatrix} = ({\color{red}0.6} * {\color{red}0}) + (-{\color{green}0.8} * {\color{green}1}) + ({\color{blue}0} * {\color{blue}0}) = -0.8 \] + + To calculate the degree between both these unit vectors we use the inverse of the cosine function \(cos^{-1}\) and this results in <code>143.1</code> degrees. We now effectively calculated the angle between these two vectors. The dot product proves very useful when doing lighting calculations later on. + 2つのベクトルのなす角を求めるにはコサインの逆関数\(cos^{-1}\)を利用します。今回の場合この値は<code>143.1</code>度です。2つのベクトルのなす角度を効率よく計算できるようになりました。内積は後程でてくる照明の計算において非常に役立ちます。 +</p> + +<h3>Cross product</h3> +<h3>外積</h3> +<p> + The cross product is only defined in 3D space and takes two non-parallel vectors as input and produces a third vector that is orthogonal to both the input vectors. If both the input vectors are orthogonal to each other as well, a cross product would result in 3 orthogonal vectors; this will prove useful in the upcoming chapters. The following image shows what this looks like in 3D space: + 外積は3次元空間においてのみ定義され、2つの平行でないベクトルからその両方に直交する3つめのベクトルを生成します。はじめの2つのベクトルが直交する場合、外積を取ることで3つの互いに直交するベクトルが得られます。この性質については次の章においてその利便性が明らかになります。次の図はここで述べたことを3次元空間に図示したものです: +</p> + +<img src="/img/getting-started/vectors_crossproduct.png" class="clean"/> + +<p> + Unlike the other operations, the cross product isn't really intuitive without delving into linear algebra so it's best to just memorize the formula and you'll be fine (or don't, you'll probably be fine as well). Below you'll see the cross product between two orthogonal vectors A and B: + 他の演算と違い、外積の計算は線形代数に踏み込まなければあまり直感的ではありません。とりあえず公式を暗記すれば十分です(覚えられなくてもおそらく大丈夫です)。以下の式は2つの直交するベクトルAとBの外積を表すものです: + + \[\begin{pmatrix} {\color{red}A_{x}} \\ {\color{green}A_{y}} \\ {\color{blue}A_{z}} \end{pmatrix} \times \begin{pmatrix} {\color{red}B_{x}} \\ {\color{green}B_{y}} \\ {\color{blue}B_{z}} \end{pmatrix} = \begin{pmatrix} {\color{green}A_{y}} \cdot {\color{blue}B_{z}} - {\color{blue}A_{z}} \cdot {\color{green}B_{y}} \\ {\color{blue}A_{z}} \cdot {\color{red}B_{x}} - {\color{red}A_{x}} \cdot {\color{blue}B_{z}} \\ {\color{red}A_{x}} \cdot {\color{green}B_{y}} - {\color{green}A_{y}} \cdot {\color{red}B_{x}} \end{pmatrix} \] + + As you can see, it doesn't really seem to make sense. However, if you just follow these steps you'll get another vector that is orthogonal to your input vectors. + これを見てもよく意味が分からないでしょう。しかしこの手順に従えば2つのベクトルからそれらに直交するベクトルが得られます。 +</p> + +<h1>Matrices</h1> +<h1>行列</h1> +<p> + Now that we've discussed almost all there is to vectors it is time to enter the matrix! + A matrix is a rectangular array of numbers, symbols and/or mathematical expressions. Each individual item in a matrix is called an <def>element</def> of the matrix. An example of a 2x3 matrix is shown below: + ベクトルに関してはだいたい見終わったので、次は行列の話に入りましょう。行列とは数字や記号、数式を四角形に並べたものです。行列を構成する個々の数字等は行列の<def>要素</def>と呼ばれます。2x3の行列の例を以下に挙げます: + + \[\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}\] + + Matrices are indexed by <code>(i,j)</code> where <code>i</code> is the row and <code>j</code> is the column, that is why the above matrix is called a 2x3 matrix (3 columns and 2 rows, also known as the <def>dimensions</def> of the matrix). This is the opposite of what you're used to when indexing 2D graphs as <code>(x,y)</code>. To retrieve the value 4 we would index it as <code>(2,1)</code> (second row, first column). + 行列の各要素は行を表わす<code>i</code>と列を表わす<code>j</code>を用いて<code>(i, j)</code>で参照できます。上の行列は2行3列なので2x3の行列と書きました。この2x3は行列の<def>型</def>と呼ばれます。これは2次元平面上で<code>(x, y)</code>を用いる場合と反対です。上の行列において4という要素を参照するには<code>(2, 1)</code>を利用します(2行目、1列目)。 +</p> + +<p> + Matrices are basically nothing more than that, just rectangular arrays of mathematical expressions. They do have a very nice set of mathematical properties and just like vectors we can define several operations on matrices, namely: addition, subtraction and multiplication. + 行列は単にこれだけのものです。数式を四角形に並べたものでしかありません。しかし行列は数学的に非常に優れた性質を持っていて、ベクトルの場合と同様に和、差、積といった演算を定義できます。 +</p> + +<h2>Addition and subtraction</h2> +<h2>行列の和、差</h2> +<p> + Matrix addition and subtraction between two matrices is done on a per-element basis. So the same general rules apply that we're familiar with for normal numbers, but done on the elements of both matrices with the same index. This does mean that addition and subtraction is only defined for matrices of the same dimensions. A 3x2 matrix and a 2x3 matrix (or a 3x3 matrix and a 4x4 matrix) cannot be added or subtracted together. Let's see how matrix addition works on two 2x2 matrices: + 2つの行列の和と差は要素毎に行われます。なので通常の数字と同じような規則が各要素毎に成り立ちます。要素毎に行われるため、和と差は同じ型の行列に対してのみ定義できます。3x2の行列と2x3の行列、あるいは3x3の行列と4x4の行列は足したり引いたりすることができません。2x2の行列どうしの足し算を以下に示します: + + \[\begin{bmatrix} {\color{red}1} & {\color{red}2} \\ {\color{green}3} & {\color{green}4} \end{bmatrix} + \begin{bmatrix} {\color{red}5} & {\color{red}6} \\ {\color{green}7} & {\color{green}8} \end{bmatrix} = \begin{bmatrix} {\color{red}1} + {\color{red}5} & {\color{red}2} + {\color{red}6} \\ {\color{green}3} + {\color{green}7} & {\color{green}4} + {\color{green}8} \end{bmatrix} = \begin{bmatrix} {\color{red}6} & {\color{red}8} \\ {\color{green}10} & {\color{green}12} \end{bmatrix} \] + +The same rules apply for matrix subtraction: +同様にして差は以下のようになります: + + \[\begin{bmatrix} {\color{red}4} & {\color{red}2} \\ {\color{green}1} & {\color{green}6} \end{bmatrix} - \begin{bmatrix} {\color{red}2} & {\color{red}4} \\ {\color{green}0} & {\color{green}1} \end{bmatrix} = \begin{bmatrix} {\color{red}4} - {\color{red}2} & {\color{red}2} - {\color{red}4} \\ {\color{green}1} - {\color{green}0} & {\color{green}6} - {\color{green}1} \end{bmatrix} = \begin{bmatrix} {\color{red}2} & -{\color{red}2} \\ {\color{green}1} & {\color{green}5} \end{bmatrix} \] + +</p> + +<h2>Matrix-scalar products</h2> +<h2>行列とスカラの積</h2> +<p> + A matrix-scalar product multiples each element of the matrix by a scalar. The following example illustrates the multiplication: + 行列とスカラの積は行列の各要素にスカラを掛け合わせることとして定義されます。以下にこの積の例を示します: + + \[{\color{green}2} \cdot \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} = \begin{bmatrix} {\color{green}2} \cdot 1 & {\color{green}2} \cdot 2 \\ {\color{green}2} \cdot 3 & {\color{green}2} \cdot 4 \end{bmatrix} = \begin{bmatrix} 2 & 4 \\ 6 & 8 \end{bmatrix}\] + +Now it also makes sense as to why those single numbers are called scalars. A scalar basically <em>scales</em> all the elements of the matrix by its value. In the previous example, all elements were scaled by <code>2</code>. +これを見るとなぜ数字がスカラと呼ばれるのか分かります。スカラは行列の各要素をその値で<em>拡大、縮小</em>(英語でscaleと言います)するのです。前の例では各要素を<code>2</code>倍に拡大しています。 +</p> + +<p> + So far so good, all of our cases weren't really too complicated. That is, until we start on matrix-matrix multiplication. + ここまではそんなに難しくはありません。続いてもう少し込み入った、行列どうしの積の話に移りましょう。 +</p> + +<h2>Matrix-matrix multiplication</h2> +<h2>行列の積</h2> +<p> + Multiplying matrices is not necessarily complex, but rather difficult to get comfortable with. Matrix multiplication basically means to follow a set of pre-defined rules when multiplying. There are a few restrictions though: + 2つの行列の積は必ずしも複雑ではありませんが、慣れるまで時間がかかります。行列の積はある規則に従った演算です。この演算にもある制約があります: + + <ol> + <li>You can only multiply two matrices if the number of columns on the left-hand side matrix is equal to the number of rows on the right-hand side matrix.</li> + <li>行列の積は、1つ目の行列の列の数と、2つ目の行列の行の数が一致する時に限り定義される。</li> + <li>Matrix multiplication is not <def>commutative</def> that is \(A \cdot B \neq B \cdot A\).</li> + <li>行列の積は<def>非可換</def>である。つまり、\(A \cdot B \neq B \cdot A\)。</li> + </ol> +</p> + +<p> + Let's get started with an example of a matrix multiplication of 2 <code>2x2</code> matrices: + まずは2つの<code>2x2</code>の行列の積を例示しましょう: + + \[ \begin{bmatrix} {\color{red}1} & {\color{red}2} \\ {\color{green}3} & {\color{green}4} \end{bmatrix} \cdot \begin{bmatrix} {\color{blue}5} & {\color{purple}6} \\ {\color{blue}7} & {\color{purple}8} \end{bmatrix} = \begin{bmatrix} {\color{red}1} \cdot {\color{blue}5} + {\color{red}2} \cdot {\color{blue}7} & {\color{red}1} \cdot {\color{purple}6} + {\color{red}2} \cdot {\color{purple}8} \\ {\color{green}3} \cdot {\color{blue}5} + {\color{green}4} \cdot {\color{blue}7} & {\color{green}3} \cdot {\color{purple}6} + {\color{green}4} \cdot {\color{purple}8} \end{bmatrix} = \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix} \] + + Right now you're probably trying to figure out what the hell just happened? Matrix multiplication is a combination of normal multiplication and addition using the left-matrix's rows with the right-matrix's columns. Let's try discussing this with the following image: + いったい何ごとかと身構えることでしょう。行列の積は左の行列の行と右の行列の列を用いて通常の和と積を組み合わせることで計算されます。下図で説明します: +</p> + + <img src="/img/getting-started/matrix_multiplication.png" class="clean"/> + +<p> + We first take the upper row of the left matrix and then take a column from the right matrix. The row and column that we picked decides which output value of the resulting <code>2x2</code> matrix we're going to calculate. If we take the first row of the left matrix the resulting value will end up in the first row of the result matrix, then we pick a column and if it's the first column the result value will end up in the first column of the result matrix. This is exactly the case of the red pathway. To calculate the bottom-right result we take the bottom row of the first matrix and the rightmost column of the second matrix. + まず左の行列から上の行を取り、次に右の行列から列を取ります。どの行とどの列を取ったかにより、結果となる<code>2x2</code>行列のどの要素を計算するのかが決まります。左の行列の1つ目の行を取ると、結果となる行列の1つ目の行を計算することになります。そして右の行列の1つ目の列を取ると、結果となる行列の1つ目の列を計算することになります。これは図において赤で囲ったものにあたります。右下の要素を計算するには、左の行列の下の行と、右の行列の右の列から計算します。 +</p> + +<p> + To calculate the resulting value we multiply the first element of the row and column together using normal multiplication, we do the same for the second elements, third, fourth etc. The results of the individual multiplications are then summed up and we have our result. Now it also makes sense that one of the requirements is that the size of the left-matrix's columns and the right-matrix's rows are equal, otherwise we can't finish the operations! + 結果となる数値を得るためには左右の行列から取った行と列の1つ目の要素どうしを掛け、2つ目の要素どうしを掛け、3つ目、4つ目...、最後にこれらの掛け合わせた値を全て足し合わせます。この操作が必要なので、1つ目の制約である、左の行列の行の数と右の行列の列の数が等しくないといけないという意味が理解できるでしょう。この制約がないと、計算を完了できません。 +</p> + +<p> + The result is then a matrix that has dimensions of (<code>n,m</code>) where <code>n</code> is equal to the number of rows of the left-hand side matrix and <code>m</code> is equal to the columns of the right-hand side matrix. + 左の行列の行数を<code>n</code>、右の行列の列数を<code>m</code>とした場合、行列の積の結果得られる行列の型は<code>(n, m)</code>となります。 +</p> + +<p> + Don't worry if you have difficulties imagining the multiplications inside your head. Just keep trying to do the calculations by hand and return to this page whenever you have difficulties. Over time, matrix multiplication becomes second nature to you. + 頭の中でこの積を想像するのが難しくても心配しないで下さい。紙に書いて計算をすればいいですし、忘れたらこのページに戻ってきてください。時間と共に慣れることでしょう。 +</p> + +<p> + Let's finish the discussion of matrix-matrix multiplication with a larger example. Try to visualize the pattern using the colors. As a useful exercise, see if you can come up with your own answer of the multiplication and then compare them with the resulting matrix (once you try to do a matrix multiplication by hand you'll quickly get the grasp of them). + もう少し大きな例で行列の積の議論を終えましょう。色分けにより計算法を可視化しています。いい演習として自分で行列の積を計算して答えを示し合わせて下さい。実際に手を動かすことで、計算の流れがよく掴めます。 + + \[ \begin{bmatrix} {\color{red}4} & {\color{red}2} & {\color{red}0} \\ {\color{green}0} & {\color{green}8} & {\color{green}1} \\ {\color{blue}0} & {\color{blue}1} & {\color{blue}0} \end{bmatrix} \cdot \begin{bmatrix} {\color{red}4} & {\color{green}2} & {\color{blue}1} \\ {\color{red}2} & {\color{green}0} & {\color{blue}4} \\ {\color{red}9} & {\color{green}4} & {\color{blue}2} \end{bmatrix} = \begin{bmatrix} {\color{red}4} \cdot {\color{red}4} + {\color{red}2} \cdot {\color{red}2} + {\color{red}0} \cdot {\color{red}9} & {\color{red}4} \cdot {\color{green}2} + {\color{red}2} \cdot {\color{green}0} + {\color{red}0} \cdot {\color{green}4} & {\color{red}4} \cdot {\color{blue}1} + {\color{red}2} \cdot {\color{blue}4} + {\color{red}0} \cdot {\color{blue}2} \\ {\color{green}0} \cdot {\color{red}4} + {\color{green}8} \cdot {\color{red}2} + {\color{green}1} \cdot {\color{red}9} & {\color{green}0} \cdot {\color{green}2} + {\color{green}8} \cdot {\color{green}0} + {\color{green}1} \cdot {\color{green}4} & {\color{green}0} \cdot {\color{blue}1} + {\color{green}8} \cdot {\color{blue}4} + {\color{green}1} \cdot {\color{blue}2} \\ {\color{blue}0} \cdot {\color{red}4} + {\color{blue}1} \cdot {\color{red}2} + {\color{blue}0} \cdot {\color{red}9} & {\color{blue}0} \cdot {\color{green}2} + {\color{blue}1} \cdot {\color{green}0} + {\color{blue}0} \cdot {\color{green}4} & {\color{blue}0} \cdot {\color{blue}1} + {\color{blue}1} \cdot {\color{blue}4} + {\color{blue}0} \cdot {\color{blue}2} \end{bmatrix} + \\ = \begin{bmatrix} 20 & 8 & 12 \\ 25 & 4 & 34 \\ 2 & 0 & 4 \end{bmatrix}\] +</p> + +<p> + As you can see, matrix-matrix multiplication is quite a cumbersome process and very prone to errors (which is why we usually let computers do this) and this gets problematic real quick when the matrices become larger. If you're still thirsty for more and you're curious about some more of the mathematical properties of matrices I strongly suggest you take a look at these <a href="https://www.khanacademy.org/math/algebra2/algebra-matrices" target="_blank">Khan Academy videos</a> about matrices. + ご覧のように行列の積は非常に面倒であり計算間違いも起こりやすく、特に大きいサイズの行列では大変ですので、通常この計算はコンピュータに任せます。行列の数学的な性質についてさらに知りたい方にはこの行列に関する動画<a href="https://www.khanacademy.org/math/algebra2/algebra-matrices" target="_blank">Khan Academy videos</a>を強くおすすめします。 +</p> + +<p> + Anyways, now that we know how to multiply matrices together, we can start getting to the good stuff. + ともあれ行列を掛け合わせることができるようになったので、面白いことができるようになりました。 +</p> + +<h1>Matrix-Vector multiplication</h1> +<h1>行列とベクトルの積</h1> +<p> + Up until now we've had our fair share of vectors. We used them to represent positions, colors and even texture coordinates. Let's move a bit further down the rabbit hole and tell you that a vector is basically a <code>Nx1</code> matrix where <code>N</code> is the vector's number of components (also known as an <def>N-dimensional</def> vector). If you think about it, it makes a lot of sense. Vectors are just like matrices an array of numbers, but with only 1 column. So, how does this new piece of information help us? Well, if we have a <code>MxN</code> matrix we can multiply this matrix with our <code>Nx1</code> vector, since the columns of the matrix are equal to the number of rows of the vector, thus matrix multiplication is defined. + ここまでベクトルについはたくさん利用してきました。ベクトルを使って位置、色、そしてテクスチャ座標を表しました。もう少し踏み込むと、ベクトルは基本的には<code>Nx1</code>の行列だと言えます。ただし<code>N</code>はベクトルの要素数で、このベクトルは<def>N次元</def>であるとも言います。この見方をするとベクトルは行列と同じく数字の羅列であり、その列が1つだけのものだと言えます。つまり、<code>MxN</code>の行列を持ってくればこの行列を<code>Nx1</code>のベクトルに掛け合わせることができるということです。この行列の列の数が、ベクトルの行の数に一致するので、行列の積が定義できるためです。 +</p> + +<p> + But why do we care if we can multiply matrices with a vector? Well, it just so happens that there are lots of interesting 2D/3D transformations we can place inside a matrix, and multiplying that matrix with a vector then <em>transforms</em> that vector. In case you're still a bit confused, let's start with a few examples and you'll soon see what we mean. + しかし行列とベクトルの積が計算できてなにがいいのでしょう。実は2次元や3次元空間での座標変換に対応した行列をベクトルに掛けることでそのベクトルを<em>座標変換</em>することができるのです。まだ少し混乱している読者のためにいくつか例を挙げます。そのうち何が起こっているのか見えてくるでしょう。 +</p> + +<h2>Identity matrix</h2> +<h2>単位行列</h2> +<p> + In OpenGL we usually work with <code>4x4</code> transformation matrices for several reasons and one of them is that most of the vectors are of size 4. The most simple transformation matrix that we can think of is the <def>identity matrix</def>. The identity matrix is an <code>NxN</code> matrix with only 0s except on its diagonal. As you'll see, this transformation matrix leaves a vector completely unharmed: + OpenGLにおいては通常<code>4x4</code>の変換行列を利用します。そのひとつの理由はほとんどの場合4次元のベクトルを扱うからです。想像し得る最も単純な変換行列は<def>単位行列</def>です。単位行列は対角成分以外の要素が0である<code>NxN</code>の行列です。この変換行列は以下のようにベクトルを全く変化させません: + + \[ \begin{bmatrix} {\color{red}1} & {\color{red}0} & {\color{red}0} & {\color{red}0} \\ {\color{green}0} & {\color{green}1} & {\color{green}0} & {\color{green}0} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}1} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{bmatrix} 1 \\ 2 \\ 3 \\ 4 \end{bmatrix} = \begin{bmatrix} {\color{red}1} \cdot 1 \\ {\color{green}1} \cdot 2 \\ {\color{blue}1} \cdot 3 \\ {\color{purple}1} \cdot 4 \end{bmatrix} = \begin{bmatrix} 1 \\ 2 \\ 3 \\ 4 \end{bmatrix} \] + + The vector is completely untouched. This becomes obvious from the rules of multiplication: the first result element is each individual element of the first row of the matrix multiplied with each element of the vector. Since each of the row's elements are 0 except the first one, we get: \({\color{red}1\cdot1} + {\color{red}0\cdot2} + {\color{red}0\cdot3} + {\color{red}0\cdot4} = 1\) and the same applies for the other 3 elements of the vector. + ベクトルは完全に元のままです。これは積の定義から明らかです。結果となるベクトルの1つ目の要素は行列の1行目にベクトルの各要素を掛けたものです。行列の一行目は1つめの要素を除いて0なので、得られる値は\({\color{red}1}\cdot1 + {\color{red}0}\cdot2 + {\color{red}0}\cdot3 + {\color{red}0}\cdot4 = 1\)となり、残りの3つの要素についても同様です。 +</p> + +<note> + You may be wondering what the use is of a transformation matrix that does not transform? The identity matrix is usually a starting point for generating other transformation matrices and if we dig even deeper into linear algebra, a very useful matrix for proving theorems and solving linear equations. + 変換しない変換行列にどんな使い道があるのか疑問に思うかもしれません。単位行列は通常他の変換行列を生成する出発点となります。あるいは線形代数に踏み込むなら、定理を証明したり線形方程式を解いたりするのに役立ちます。 +</note> + +<h2>Scaling</h2> +<h2>拡大</h2> +<p> + When we're scaling a vector we are increasing the length of the arrow by the amount we'd like to scale, keeping its direction the same. Since we're working in either 2 or 3 dimensions we can define scaling by a vector of 2 or 3 scaling variables, each scaling one axis (<code>x</code>, <code>y</code> or <code>z</code>). + ベクトルを拡大するには矢印の方向を一定に保ったまま、長さに拡大率を掛ける必要があります。2次元あるいは3次元の空間を扱っているので、2または3の値を持つベクトルにより拡大を定義できます。そのベクトルの各要素がそれぞれ<code>x</code>、<code>y</code>、<code>z</code>軸をそれぞれ拡大します。 +</p> + +<p> + Let's try scaling the vector \({\color{red}\bar{v}} = (3,2)\). We will scale the vector along the x-axis by <code>0.5</code>, thus making it twice as narrow; and we'll scale the vector by <code>2</code> along the y-axis, making it twice as high. Let's see what it looks like if we scale the vector by <code>(0.5,2)</code> as \({\color{blue}\bar{s}}\): + \({\color{red}\bar{v}} = (3, 2)\)を拡大してみましょう。x軸方向に<code>0.5</code>倍、つまり半分に切り詰め、y軸方向に<code>2</code>倍、つまり倍に引き伸ばします。このベクトルを<code>(0.5, 2)</code>により拡大したベクトル\({\color{blue}\bar{s}}\)は以下のようになります: +</p> + + +<img src="/img/getting-started/vectors_scale.png" class="clean"/> + +<p> + Keep in mind that OpenGL usually operates in 3D space so for this 2D case we could set the z-axis scale to <code>1</code>, leaving it unharmed. The scaling operation we just performed is a <def>non-uniform</def> scale, because the scaling factor is not the same for each axis. If the scalar would be equal on all axes it would be called a <def>uniform scale</def>. + OpenGLは3次元を扱うので、この2次元の例においてはz軸方向の拡大率は<code>1</code>として、その軸は変化させていないことに注意してください。今回行った拡大は各要素に対する拡大率が違うので、<def>一様でない</def>拡大と呼ばれます。これに対して各軸の拡大率が同じ場合、<def>一様な</def>拡大と言います。 +</p> + +<p> + Let's start building a transformation matrix that does the scaling for us. We saw from the identity matrix that each of the diagonal elements were multiplied with its corresponding vector element. What if we were to change the <code>1</code>s in the identity matrix to <code>3</code>s? In that case, we would be multiplying each of the vector elements by a value of <code>3</code> and thus effectively uniformly scale the vector by 3. If we represent the scaling variables as \( ({\color{red}S_1}, {\color{green}S_2}, {\color{blue}S_3}) \) we can define a scaling matrix on any vector \((x,y,z)\) as: + それでは拡大を行う変換行列を作成してみましょう。先程単位行列を見た際、対角線上の各要素がそれに対応するベクトルの要素と掛け合わせられるのを確認しました。単位行列の対角成分を<code>1</code>ではなく<code>3</code>にしたらどうなるでしょう。この場合、ベクトルの各要素に<code>3</code>が掛けられ、ベクトルが一様に3倍になります。各軸に対する拡大率をそれぞれ\( ({\color{red}S_1}, {\color{green}S_2}, {\color{blue}S_3})\)とした場合、ベクトル\((x, y, z)\)に対する拡大行列は: + + \[\begin{bmatrix} {\color{red}S_1} & {\color{red}0} & {\color{red}0} & {\color{red}0} \\ {\color{green}0} & {\color{green}S_2} & {\color{green}0} & {\color{green}0} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}S_3} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} {\color{red}S_1} \cdot x \\ {\color{green}S_2} \cdot y \\ {\color{blue}S_3} \cdot z \\ 1 \end{pmatrix} \] + + Note that we keep the 4th scaling value <code>1</code>. The <code>w</code> component is used for other purposes as we'll see later on. + となります。4つ目の要素は<code>1</code>であることに注意して下さい。この<code>w</code>要素は後程別の用途に利用します。 +</p> + +<h2>Translation</h2> +<h2>平行移動</h2> +<p> + <def>Translation</def> is the process of adding another vector on top of the original vector to return a new vector with a different position, thus <em>moving</em> the vector based on a translation vector. We've already discussed vector addition so this shouldn't be too new. + <def>平行移動</def>とはあるベクトルに他のベクトルを足して新しいベクトルにすることで、元のベクトルを平行移動ベクトルにより<em>移動</em>させることです。既にベクトルの足し算については確認しているので、特に新しいことはありません。 +</p> + +<p> + Just like the scaling matrix there are several locations on a 4-by-4 matrix that we can use to perform certain operations and for translation those are the top-3 values of the 4th column. If we represent the translation vector as \(({\color{red}T_x},{\color{green}T_y},{\color{blue}T_z})\) we can define the translation matrix by: + 拡大行列と同様、4x4の行列のある部分を利用することで平行移動を実現できます。その部分とは4番目の列の上3つの要素です。平行移動ベクトルを\(({\color{red}T_x},{\color{green}T_y},{\color{blue}T_z})\)で表わすと、平行移動行列は: + + \[\begin{bmatrix} {\color{red}1} & {\color{red}0} & {\color{red}0} & {\color{red}T_x} \\ {\color{green}0} & {\color{green}1} & {\color{green}0} & {\color{green}T_y} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}1} & {\color{blue}T_z} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x + {\color{red}T_x} \\ y + {\color{green}T_y} \\ z + {\color{blue}T_z} \\ 1 \end{pmatrix} \] + + This works because all of the translation values are multiplied by the vector's <code>w</code> column and added to the vector's original values (remember the matrix-multiplication rules). This wouldn't have been possible with a 3-by-3 matrix. + のようになります。平行移動の値はベクトルの<code>w</code>要素に掛けられ、ベクトルの元の値に足し合せられます(行列の積の規則を思い出して下さい)。これは3x3の行列では実現できません。 +</p> + +<note> + <strong>Homogeneous coordinates</strong><br/> + <strong>同次座標</strong><br/> + The <code>w</code> component of a vector is also known as a <def>homogeneous coordinate</def>. + To get the 3D vector from a homogeneous vector we divide the <code>x</code>, <code>y</code> and <code>z</code> coordinate by its <code>w</code> coordinate. We usually do not notice this since the <code>w</code> component is <code>1.0</code> most of the time. Using homogeneous coordinates has several advantages: it allows us to do matrix translations on 3D vectors (without a <code>w</code> component we can't translate vectors) and in the next chapter we'll use the <code>w</code> value to create 3D perspective.<br/> + <br/> + Also, whenever the homogeneous coordinate is equal to <code>0</code>, the vector is specifically known as a <def>direction vector</def> since a vector with a <code>w</code> coordinate of <code>0</code> cannot be translated. + ベクトルの<code>w</code>要素は<def>同次座標</def>として知られます。同次ベクトルから3次元の座標を得るには<code>x</code>、<code>y</code>、<code>z</code>座標を<code>w</code>座標で割ります。多くの場合<code>w</code>座標は<code>1.0</code>なので、通常気にしなくてもかまいません。同次座標の利点は3次元のベクトルに対して行列を掛けることで平行移動できる(<code>w</code>要素がないと実現できません)ことや、次の章ででてきますが、<code>w</code>の値を利用して3次元空間の透視投影を行います。<br/><br/> + また、同次座標が<code>0</code>のベクトルは特別に<def>方向ベクトル</def>と呼ばれます。<code>w</code>要素が<code>0</code>のベクトルは平行移動できないためです。 +</note> + +<p> + With a translation matrix we can move objects in any of the 3 axis directions (<code>x</code>, <code>y</code>, <code>z</code>), making it a very useful transformation matrix for our transformation toolkit. + 平行移動行列を利用して<code>x</code>、<code>y</code>、<code>z</code>の任意の方向に物体を動かすことができ、変換行列のひとつとして非常に有用です。 +</p> + +<h2>Rotation</h2> +<h2>回転</h2> +<p> + The last few transformations were relatively easy to understand and visualize in 2D or 3D space, but rotations are a bit trickier. If you want to know exactly how these matrices are constructed I'd recommend that you watch the rotation items of Khan Academy's <a href="https://www.khanacademy.org/math/linear-algebra/matrix_transformations" target="_blank">linear algebra</a> videos. + ここまでの変換は理解し、2次元や3次元空間で可視化すること比較的簡単でしたでしたが、回転はもう少しトリッキーです。回転に用いる行列がどうしてそのような形になっているのかを正確に理解したい場合、Khan Academyによる<a href="https://www.khanacademy.org/math/linear-algebra/matrix_transformations" target="_blank">線形代数</a>の動画の、物体の回転の部分をご覧になるのがいいでしょう。 +</p> + +<p> + First let's define what a rotation of a vector actually is. A rotation in 2D or 3D is represented with an <def>angle</def>. An angle could be in degrees or radians where a whole circle has 360 degrees or 2 <a href="http://en.wikipedia.org/wiki/Pi" target="_blank">PI</a> radians. I prefer explaining rotations using degrees as we're generally more accustomed to them. + まずはじめにベクトルの回転がどんなものなのかを定義しましょう。2次元あるいは3次元における回転は<def>角度</def>によって規定されます。角度は一周を360度とする度数法を用いた度または一周を2<a href="http://en.wikipedia.org/wiki/Pi" target="_blank">\(\pi\)</a>とする弧度法を用いたラジアンにより表わされます。度数法に慣れている人が多いでしょうから、ここでは度数法で説明します。 + +<note> + Most rotation functions require an angle in radians, but luckily degrees are easily converted to radians: <br/> + <code>angle in degrees = angle in radians * (180 / PI) </code><br/> + <code>angle in radians = angle in degrees * (PI / 180) </code><br/> + Where <code>PI</code> equals (rounded) <code>3.14159265359</code>. + 回転に用いる多くの関数はラジアンで角度を指定します。しかし度は簡単にラジアンに変換できます:<br/> + <code>度 = ラジアン * (180 / \(\pi\)) </code><br/> + <code>ラジアン = 度 * (\(\pi\) / 180) </code><br/> + ここで<code>\(\pi\)</code>はおよそ<code>3.14159265359</code>です。 +</note> + + Rotating half a circle rotates us 360/2 = 180 degrees and rotating 1/5th to the right means we rotate 360/5 = 72 degrees to the right. This is demonstrated for a basic 2D vector where \({\color{red}\bar{v}}\) is rotated 72 degrees to the right, or clockwise, from \({\color{green}\bar{k}}\): + 半円分の回転は360/2 = 180度の回転を意味し、右に1/5回転させるのは360/5 = 72度時計回りに回転させることを意味します。以下にこの回転を2次元ベクトルで行ったものを図示します。\({\color{red}\bar{v}}\)は\({\color{green}\bar{k}}\)を時計回りに72度回転させたものです: +</p> + + <img src="/img/getting-started/vectors_angle.png" class="clean" /> + +<p> + Rotations in 3D are specified with an angle <strong>and</strong> a <def>rotation axis</def>. The angle specified will rotate the object along the rotation axis given. Try to visualize this by spinning your head a certain degree while continually looking down a single rotation axis. When rotating 2D vectors in a 3D world for example, we set the rotation axis to the z-axis (try to visualize this). + 3次元空間における回転は角度に<strong>加え</strong><def>回転軸</def>により規定されます。物体は回転軸に沿って与えられた角度だけ回転します。ある回転軸を見たまま頭を一定の角度回転させて下さい。それが回転行列によるベクトルの回転です。3次元の回転を2次元に落として考える場合、回転軸をz軸に一致させます。 +</p> + +<p> + Using trigonometry it is possible to transform vectors to newly rotated vectors given an angle. This is usually done via a smart combination of the <code>sine</code> and <code>cosine</code> functions (commonly abbreviated to <code>sin</code> and <code>cos</code>). A discussion of how the rotation matrices are generated is out of the scope of this chapter. + 三角関数を利用すればあるベクトルを与えられた角度だけ回転させることができます。これは通常<code>sine</code>と<code>cosine</code>関数の組み合わせにより行われます(これらの関数は一般に<code>sin</code>、<code>cos</code>と略記されます)。回転行列の導出はこの章の範疇を越えます。 +</p> + +<p> + A rotation matrix is defined for each unit axis in 3D space where the angle is represented as the theta symbol \(\theta\). + 3次元空間における各座標軸に沿った角度\(\theta\)度の回転行列は以下のように表わされます: +</p> + + <p> + Rotation around the X-axis: + x軸周りの回転: + + \[\begin{bmatrix} {\color{red}1} & {\color{red}0} & {\color{red}0} & {\color{red}0} \\ {\color{green}0} & {\color{green}\cos \theta} & - {\color{green}\sin \theta} & {\color{green}0} \\ {\color{blue}0} & {\color{blue}\sin \theta} & {\color{blue}\cos \theta} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x \\ {\color{green}\cos \theta} \cdot y - {\color{green}\sin \theta} \cdot z \\ {\color{blue}\sin \theta} \cdot y + {\color{blue}\cos \theta} \cdot z \\ 1 \end{pmatrix}\] + </p> + + <p> + Rotation around the Y-axis: + y軸周りの回転: + + \[\begin{bmatrix} {\color{red}\cos \theta} & {\color{red}0} & {\color{red}\sin \theta} & {\color{red}0} \\ {\color{green}0} & {\color{green}1} & {\color{green}0} & {\color{green}0} \\ - {\color{blue}\sin \theta} & {\color{blue}0} & {\color{blue}\cos \theta} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} {\color{red}\cos \theta} \cdot x + {\color{red}\sin \theta} \cdot z \\ y \\ - {\color{blue}\sin \theta} \cdot x + {\color{blue}\cos \theta} \cdot z \\ 1 \end{pmatrix} \] + </p> + + <p> + Rotation around the Z-axis: + z軸周りの回転: + + \[\begin{bmatrix} {\color{red}\cos \theta} & - {\color{red}\sin \theta} & {\color{red}0} & {\color{red}0} \\ {\color{green}\sin \theta} & {\color{green}\cos \theta} & {\color{green}0} & {\color{green}0} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}1} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} {\color{red}\cos \theta} \cdot x - {\color{red}\sin \theta} \cdot y \\ {\color{green}\sin \theta} \cdot x + {\color{green}\cos \theta} \cdot y \\ z \\ 1 \end{pmatrix} \] + </p> + + + <p> + Using the rotation matrices we can transform our position vectors around one of the three unit axes. To rotate around an arbitrary 3D axis we can combine all 3 them by first rotating around the X-axis, then Y and then Z for example. However, this quickly introduces a problem called <def>Gimbal lock</def>. We won't discuss the details, but a better solution is to rotate around an arbitrary unit axis e.g. <code>(0.662,0.2,0.722)</code> (note that this is a unit vector) right away instead of combining the rotation matrices. Such a (verbose) matrix exists and is given below with \(({\color{red}R_x}, {\color{green}R_y}, {\color{blue}R_z})\) as the arbitrary rotation axis: + これらの回転行列により、位置ベクトルを座標軸に沿って回転させることができます。任意の回転軸に沿って回転させるにはこの3つの回転を組み合わせればいいのですが、これでは<def>ジンバルロック</def>と呼ばれる問題が生じます。この問題には深く立ち入りません。よりよい方法は任意の単位ベクトルに沿って回転させる回転行列を利用することです。そのような直接的な行列は確かに存在し、\(({\color{red}R_x}, {\color{green}R_y}, {\color{blue}R_z})\)を回転軸としたとき以下のように表わされます。 + + \[\begin{bmatrix} \cos \theta + {\color{red}R_x}^2(1 - \cos \theta) & {\color{red}R_x}{\color{green}R_y}(1 - \cos \theta) - {\color{blue}R_z} \sin \theta & {\color{red}R_x}{\color{blue}R_z}(1 - \cos \theta) + {\color{green}R_y} \sin \theta & 0 \\ {\color{green}R_y}{\color{red}R_x} (1 - \cos \theta) + {\color{blue}R_z} \sin \theta & \cos \theta + {\color{green}R_y}^2(1 - \cos \theta) & {\color{green}R_y}{\color{blue}R_z}(1 - \cos \theta) - {\color{red}R_x} \sin \theta & 0 \\ {\color{blue}R_z}{\color{red}R_x}(1 - \cos \theta) - {\color{green}R_y} \sin \theta & {\color{blue}R_z}{\color{green}R_y}(1 - \cos \theta) + {\color{red}R_x} \sin \theta & \cos \theta + {\color{blue}R_z}^2(1 - \cos \theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\] + + + A mathematical discussion of generating such a matrix is out of the scope of this chapter. Keep in mind that even this matrix does not completely prevent gimbal lock (although it gets a lot harder). To truly prevent Gimbal locks we have to represent rotations using <def>quaternions</def>, that are not only safer, but also more computationally friendly. However, a discussion of quaternions is out of this chapter's scope. + この行列の数学的な導出はこの章の内容から逸脱しますので立ち入りません。またこの行列はジンバルロックを完全には解決しないことを頭の片隅に置いておいて下さい(むしろ問題はさらにややこしくなっています)。完全にジンバルロックを解消するには<def>四元数</def>を利用する必要があります。しかしこれは安全でない上に計算量も多くなります。いずれにせよこの章の範疇ではありませんのでここでは議論しません。 + </p> + +<h2>Combining matrices</h2> +<h2>行列の組み合わせ</h2> +<p> + The true power from using matrices for transformations is that we can combine multiple transformations in a single matrix thanks to matrix-matrix multiplication. Let's see if we can generate a transformation matrix that combines several transformations. Say we have a vector <code>(x,y,z)</code> and we want to scale it by 2 and then translate it by <code>(1,2,3)</code>. We need a translation and a scaling matrix for our required steps. The resulting transformation matrix would then look like: + 座標変換に行列を利用する真のパワーは、行列の積のおかげで複数の変換をひとつの行列により表わすことができることです。いくつかの変換を組み合わせた変換行列の例を見てみましょう。ここにベクトル<code>(x, y, z)</code>があり、二倍に拡大した後<code>(1, 2, 3)</code>だけ平行移動させたいとしましょう。この操作のために平行移動の行列と拡大の行列が必要です。その結果変換行列は以下のようになります: + + \[Trans . Scale = \begin{bmatrix} {\color{red}1} & {\color{red}0} & {\color{red}0} & {\color{red}1} \\ {\color{green}0} & {\color{green}1} & {\color{green}0} & {\color{green}2} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}1} & {\color{blue}3} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} . \begin{bmatrix} {\color{red}2} & {\color{red}0} & {\color{red}0} & {\color{red}0} \\ {\color{green}0} & {\color{green}2} & {\color{green}0} & {\color{green}0} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}2} & {\color{blue}0} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} = \begin{bmatrix} {\color{red}2} & {\color{red}0} & {\color{red}0} & {\color{red}1} \\ {\color{green}0} & {\color{green}2} & {\color{green}0} & {\color{green}2} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}2} & {\color{blue}3} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} \] + + Note that we first do a translation and then a scale transformation when multiplying matrices. Matrix multiplication is not commutative, which means their order is important. When multiplying matrices the right-most matrix is first multiplied with the vector so you should read the multiplications from right to left. It is advised to first do scaling operations, then rotations and lastly translations when combining matrices otherwise they may (negatively) affect each other. For example, if you would first do a translation and then scale, the translation vector would also scale! + 行列の積において初めに平行移動をしてその後拡大をしていることに注意して下さい。行列の積は非可換なので順番が大切です。行列の積において、ベクトルに対してまず右側の行列から掛けられるので、行列の積は右から左に見ていかなければいけません。行列を組み合わせる際、まず拡大の操作を行い、その後平行移動の操作に移って下さい。そうしないとお互いに間違った効果が生じてしまいます。例えば平行移動を行った後に拡大すると、平行移動のベクトル自体に拡大の影響がでてしいます。 + </p> + +<p> + Running the final transformation matrix on our vector results in the following vector: + 組み合わせた結果の変換行列をベクトルに掛けると以下のような結果が得られます: + + \[\begin{bmatrix} {\color{red}2} & {\color{red}0} & {\color{red}0} & {\color{red}1} \\ {\color{green}0} & {\color{green}2} & {\color{green}0} & {\color{green}2} \\ {\color{blue}0} & {\color{blue}0} & {\color{blue}2} & {\color{blue}3} \\ {\color{purple}0} & {\color{purple}0} & {\color{purple}0} & {\color{purple}1} \end{bmatrix} . \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} {\color{red}2}x + {\color{red}1} \\ {\color{green}2}y + {\color{green}2} \\ {\color{blue}2}z + {\color{blue}3} \\ 1 \end{bmatrix} \] + + Great! The vector is first scaled by two and then translated by <code>(1,2,3)</code>. + ええやん。ベクトルはまず2倍に拡大され、<code>(1, 2, 3)</code>だけ平行いどうしました。 + </p> + +<h1>In practice</h1> +<h1>実践</h1> +<p> + Now that we've explained all the theory behind transformations, it's time to see how we can actually use this knowledge to our advantage. OpenGL does not have any form of matrix or vector knowledge built in, so we have to define our own mathematics classes and functions. In this book we'd rather abstract from all the tiny mathematical details and simply use pre-made mathematics libraries. Luckily, there is an easy-to-use and tailored-for-OpenGL mathematics library called GLM. + 変換に関する理論は説明し終わったので、この知識を実際に利用してみましょう。OpenGLには行列やベクトルの概念が組込まれていないので、自分達で数学的なクラスや関数を定義しないといけません。ここでは自分達で数学的に込み入ったものを作成するのではなく、あらかじめ用意された数学のライブラリを利用することにします。ありがたいことに簡単に使えてOpenGLに特化したGLMというライブラリが存在します。 + </p> + + <h2>GLM</h2> + <h2>GLM</h2> +<p> + <img src="/img/getting-started/glm.png" class="right"/> + GLM stands for Open<strong>GL</strong> <strong>M</strong>athematics and is a <em>header-only</em> library, which means that we only have to include the proper header files and we're done; no linking and compiling necessary. + GLM can be downloaded from their <a href="https://glm.g-truc.net/0.9.8/index.html" target="_blank">website</a>. Copy the root directory of the header files into your <em>includes</em> folder and let's get rolling. + GLMはOpen<strong>GL</strong> <strong>M</strong>athematicsの省略で、<em>ヘッダーのみ</em>のライブラリです。つまり必要なヘッダーファイルをインクルードするだけでリンクやコンパイルが必要ありません。GLMは彼らの<a href="https://glm.g-truc.net/0.9.8/index.html" target="_blank">ウェブサイト</a>からダウンロードできます。ヘッダーファイルのルートディレクトリを手元の<em>include</em>フォルダにコピーすればそれだけで利用できます。 + </p> + +<!--<warning> + Since GLM version <code>0.9.9</code>, GLM default initializates matrix types to a 0-initalized matrix, instead of the identity matrix. From that version it is required to initialize matrix types as: <code>glm::mat4 mat = glm::mat4(1.0f)</code>. + + For consistency with the tutorials' code it's advised to use a version of GLM lower than <code>0.9.9</code> or initialize all matrices as mentioned above. +</warning> +--> + + <p> + Most of GLM's functionality that we need can be found in 3 headers files that we'll include as follows: + ここで必要なほとんどのGLMの関数は以下の3つのヘッダーファイルに含まれます: + </p> + +<pre><code> +#include &lt;glm/glm.hpp&gt; +#include &lt;glm/gtc/matrix_transform.hpp&gt; +#include &lt;glm/gtc/type_ptr.hpp&gt; +</code></pre> + + <p> + Let's see if we can put our transformation knowledge to good use by translating a vector of <code>(1,0,0)</code> by <code>(1,1,0)</code> (note that we define it as a <code>glm::vec4</code> with its homogeneous coordinate set to <code>1.0</code>: + さっそく座標変換の知識を利用して、ベクトル<code>(1, 0, 0)</code>を<code>(1, 1, 0)</code>だけ平行移動してみましょう。これらのベクトルを<code>glm::vec4</code>により定義し、同次座標を<code>1.0</code>にしていることに注意して下さい: + </p> + +<pre><code> +glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f); +glm::mat4 trans = glm::mat4(1.0f); +trans = <function id='55'>glm::translate</function>(trans, glm::vec3(1.0f, 1.0f, 0.0f)); +vec = trans * vec; +std::cout &lt;&lt; vec.x &lt;&lt; vec.y &lt;&lt; vec.z &lt;&lt; std::endl; +</code></pre> + + <p> + We first define a vector named <code>vec</code> using GLM's built-in vector class. Next we define a <code>mat4</code> and explicitly initialize it to the identity matrix by initializing the matrix's diagonals to <code>1.0</code>; if we do not initialize it to the identity matrix the matrix would be a null matrix (all elements <code>0</code>) and all subsequent matrix operations would end up a null matrix as well. + 初めにGLMに組込まれたベクトルのクラスを用いて<code>vec</code>という名前のベクトルを定義します。次に<code>mat4</code>という行列を定義し、対角成分を<code>1.0</code>にすることで単位行列にしています。この操作をしなかった場合、行列は零行列(全ての要素が<code>0</code>の行列)になり、以降の操作は全て零行列を生成することになります。 +</p> + +<p> +The next step is to create a transformation matrix by passing our identity matrix to the <code><function id='55'>glm::translate</function></code> function, together with a translation vector (the given matrix is then multiplied with a translation matrix and the resulting matrix is returned). <br/> + Then we multiply our vector by the transformation matrix and output the result. If we still remember how matrix translation works then the resulting vector should be <code>(1+1,0+1,0+0)</code> which is <code>(2,1,0)</code>. This snippet of code outputs <code>210</code> so the translation matrix did its job. + 次に今作った単位行列を平行移動ベクトルと共に<code><function id='55'>glm::translate</function></code>に渡すことで平行移動行列を作成します。これにより渡した行列に平行移動行列が乗じられ、その結果の行列が返ります。<br/> + 得られた行列をベクトルに掛けることで、結果が得られます。平行移動の定義から、得られるベクトルは<code>(1 + 1, 0 + 1, 0 + 0)</code>つまり<code>(2, 1, 0)</code>であるはずです。このコードの出力は<code>210</code>なので、予想通り、平行移動の行列が機能しています。 + </p> + + <p> + Let's do something more interesting and scale and rotate the container object from the previous chapter: + もう少し面白いことをしてみましょう。前章の箱を拡大、回転してみます: + </p> + +<pre><code> +glm::mat4 trans = glm::mat4(1.0f); +trans = <function id='57'>glm::rotate</function>(trans, <function id='63'>glm::radians</function>(90.0f), glm::vec3(0.0, 0.0, 1.0)); +trans = <function id='56'>glm::scale</function>(trans, glm::vec3(0.5, 0.5, 0.5)); +</code></pre> + +<p> + First we scale the container by <code>0.5</code> on each axis and then rotate the container <code>90</code> degrees around the Z-axis. GLM expects its angles in radians so we convert the degrees to radians using <code><function id='63'>glm::radians</function></code>. Note that the textured rectangle is on the XY plane so we want to rotate around the Z-axis. Keep in mind that the axis that we rotate around should be a unit vector, so be sure to normalize the vector first if you're not rotating around the X, Y, or Z axis. Because we pass the matrix to each of GLM's functions, GLM automatically multiples the matrices together, resulting in a transformation matrix that combines all the transformations. + まずは箱を各軸に沿って<code>0.5</code>倍に縮小し、z軸に沿って<code>90</code>度回転させます。GLMは角度としてラジアンを受け取りますので、<code><function id='63'>glm::radians</function></code>により変換します。テクスチャの付いた四角形はxy平面にあるので回転はz軸周りで行います。また回転軸は単位行列である必要がありますので、特にx、y、またはz軸に沿った回転でない場合に正規化するのを忘れないで下さい。行列をGLMの関数に渡せば、GLMは自動的に行列を掛け合わせ、全ての変換を組み合わせた変換行列が出力されます。 + </p> + + <p> + The next big question is: how do we get the transformation matrix to the shaders? We shortly mentioned before that GLSL also has a <code>mat4</code> type. So we'll adapt the vertex shader to accept a <code>mat4</code> uniform variable and multiply the position vector by the matrix uniform: + 次に起こる疑問はどのようにして変換行列をシェーダーに渡すのかというものです。以前説明しましたが、GLSLには<code>mat4</code>という型があります。そのため頂点シェーダーが<code>mat4</code>のユニフォームを受け容れるようにし、位置ベクトルにその行列のユニフォームを乗じます: + </p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoord; + +out vec2 TexCoord; + +uniform mat4 transform; + +void main() +{ + gl_Position = transform * vec4(aPos, 1.0f); + TexCoord = vec2(aTexCoord.x, aTexCoord.y); +} +</code></pre> + +<note> + GLSL also has <code>mat2</code> and <code>mat3</code> types that allow for swizzling-like operations just like vectors. All the aforementioned math operations (like scalar-matrix multiplication, matrix-vector multiplication and matrix-matrix multiplication) are allowed on the matrix types. Wherever special matrix operations are used we'll be sure to explain what's happening. +GLSLには<code>mat2</code>や<code>mat3</code>といった型もあり、ベクトル同様、要素を入れ替えるスイズリングを利用できます。ここまで見て来た、スカラと行列の積、行列とベクトルの積、行列どうしの積といった演算は全てこの行列の型で利用することができます。特殊な行列の演算が出てきたら必ず詳細を説明するようにします。 +</note> + + <p> + We added the uniform and multiplied the position vector with the transformation matrix before passing it to <var>gl_Position</var>. Our container should now be twice as small and rotated <code>90</code> degrees (tilted to the left). We still need to pass the transformation matrix to the shader though: + <var>gl_Position</var>に位置ベクトルを渡す前に、変換行列のユニフォームを作成し、それを掛けました。これで箱は半分の大きさになり、<code>90</code>度右に回転しするはずです。あとは変換行列をシェーダーに送信しなければいけません: + </p> + +<pre><code> +unsigned int transformLoc = <function id='45'>glGetUniformLocation</function>(ourShader.ID, "transform"); +<function id='44'>glUniform</function>Matrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans)); +</code></pre> + +<p> + We first query the location of the uniform variable and then send the matrix data to the shaders using <fun><function id='44'>glUniform</function></fun> with <code>Matrix4fv</code> as its postfix. The first argument should be familiar by now which is the uniform's location. The second argument tells OpenGL how many matrices we'd like to send, which is <code>1</code>. The third argument asks us if we want to transpose our matrix, that is to swap the columns and rows. OpenGL developers often use an internal matrix layout called <def>column-major ordering</def> which is the default matrix layout in GLM so there is no need to transpose the matrices; we can keep it at <var>GL_FALSE</var>. The last parameter is the actual matrix data, but GLM stores their matrices' data in a way that doesn't always match OpenGL's expectations so we first convert the data with GLM's built-in function <fun>value_ptr</fun>. + まずユニフォーム変数の場所を特定し<fun><function id='44'>glUniform</function></fun>の語尾に<code>Matrix4fv</code>をつけた関数を用いて行列のデータをシェーダーに送信します。1つ目の引数は今まで通りユニフォームの場所です。2つ目はいくつの行列を送信するのかをOpenGLに伝えるもので、今回は<code>1</code>です。3つ目の引数は送信する行列を転置するかどうかです。行列の転置とは、行と列を入れ替えることです。OpenGLの開発者は行列の構造として<def>列優先</def>の配置を用いることが多く、この配置はGLMの既定のものでもあるので、今回は転置を取る必要はありません。この引数は<var>GL_FALSE</var>にしておきます。最後の引数は実際の行列のデータですが、GLMにおける行列のデータはOpenGLが受け付けるものとは少し違うので、GLMの組込み関数<fun>value_ptr</fun>により変換する必要があります。 +</p> + +<p> + We created a transformation matrix, declared a uniform in the vertex shader and sent the matrix to the shaders where we transform our vertex coordinates. The result should look something like this: + ここまで変換行列を作成し、頂点シェーダーにおいてユニフォームを宣言し、行列のデータをシェーダーに送信し、そこで頂点座標を変換しました。その結果以下のようなものが得られるはずです: +</p> + + <img src="/img/getting-started/transformations.png" class="clean" /> + +<p> + Perfect! Our container is indeed tilted to the left and twice as small so the transformation was successful. Let's get a little more funky and see if we can rotate the container over time, and for fun we'll also reposition the container at the bottom-right side of the window. +To rotate the container over time we have to update the transformation matrix in the render loop because it needs to update each frame. We use GLFW's time function to get an angle over time: +完璧です。画面の箱は確かに左に傾き、半分の大きさになりました。座標変換は成功です。さらにもう少しイカしたことをやってみましょう。時間と共に箱を回転させ、ウィンドウの右下に移動させることはできるでしょうか。時間と共に箱を回転させるためには変換行列をフレーム毎に更新する必要があるので、描画ループの中でその操作が必要です。時間に応じた回転角を設定するために、GLFWの時間に関する関数を利用します: +</p> + +<pre><code> +glm::mat4 trans = glm::mat4(1.0f); +trans = <function id='55'>glm::translate</function>(trans, glm::vec3(0.5f, -0.5f, 0.0f)); +trans = <function id='57'>glm::rotate</function>(trans, (float)<function id='47'>glfwGetTime</function>(), glm::vec3(0.0f, 0.0f, 1.0f)); +</code></pre> + +<p> + Keep in mind that in the previous case we could declare the transformation matrix anywhere, but now we have to create it every iteration to continuously update the rotation. This means we have to re-create the transformation matrix in each iteration of the render loop. Usually when rendering scenes we have several transformation matrices that are re-created with new values each frame. + 以前は変換行列をどこで宣言してもよかったですが、今回連続的に回転角を更新するためにフレーム毎に変換行列を作成する必要があることに注意して下さい。つまり描画ループが一周する度に変換行列を再生成しないといけません。画面を描画する際は通常、いくつかの変換行列をフレーム毎に新しい値と共に再生成することになります。 +</p> + +<p> + Here we first rotate the container around the origin <code>(0,0,0)</code> and once it's rotated, we translate its rotated version to the bottom-right corner of the screen. Remember that the actual transformation order should be read in reverse: even though in code we first translate and then later rotate, the actual transformations first apply a rotation and then a translation. Understanding all these combinations of transformations and how they apply to objects is difficult to understand. Try and experiment with transformations like these and you'll quickly get a grasp of it. + 原点<code>(0, 0, 0)</code>の周りで箱を回転させた後、平行移動により画面の右下に持って行きます。実際の変換の順番は逆から読むことを思い出して下さい。コードにおいてもまず平行移動し、その後回転させていますが、実際の変換は回転の後に平行移動です。この変換の組み合わせや、それがどのように物体に適応されるかを完全に理解するのは困難です。いろいろ実験してみて下さい。そうすればすぐに全容が掴めるはずです。 +</p> + + +<p> + If you did things right you should get the following result: + ここまで正しくプログラムできていれば以下のようになるはずです: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/getting-started/transformations.mp4" type="video/mp4" /> + <img src="/img/getting-started/transformations2.png" class="clean"/> + </video> +</div> + + + <p> + And there you have it. A translated container that's rotated over time, all done by a single transformation matrix! Now you can see why matrices are such a powerful construct in graphics land. We can define an infinite amount of transformations and combine them all in a single matrix that we can re-use as often as we'd like. Using transformations like this in the vertex shader saves us the effort of re-defining the vertex data and saves us some processing time as well, since we don't have to re-send our data all the time (which is quite slow); all we need to do is update the transformation uniform. + やりました。平行移動された箱が時間と共に回転しています。すべての変換がひとつの変換行列で行われました。グラフィックスの世界において行列がいかに強力な道具であるかということが理解できたでしょう。変換行列は好きなだけ定義してひとつにまとめることができ、その変換行列はいつでも再利用できます。このように頂点シェーダーにおいて変換行列を利用することで、いちいち頂点データを再定義する面倒を回避でき、またそのデータをシェーダーに再送する必要がなくなるので処理時間も短縮できます(データの送信は遅いのです)。必要なことは変換行列の更新だけです。 + </p> + +<p> + If you didn't get the right result or you're stuck somewhere else, take a look at the <a href="/code_viewer_gh.php?code=src/1.getting_started/5.1.transformations/transformations.cpp" target="_blank">source code</a> and the updated <a href="https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h" target="_blank">shader</a> class. + 正しい結果が得られなかったり、どこかで詰まったのであれば、<a href="/code_viewer_gh.php?code=src/1.getting_started/5.1.transformations/transformations.cpp" target="_blank">ソースコード</a>を確認して、<a href="https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader_m.h" target="_blank">シェーダー</a>クラスを更新して下さい。 +</p> + + <p> + In the next chapter we'll discuss how we can use matrices to define different coordinate spaces for our vertices. This will be our first step into 3D graphics! + 次章では行列を用いて頂点のための別の座標空間を定義する方法を議論します。3次元グラフィックスへの最初の一歩です。 + </p> + +<h2>Further reading</h2> +<h2>参考文献</h2> +<ul> + <li><a href="https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab" target="_blank">Essence of Linear Algebra</a>: great video tutorial series by Grant Sanderson about the underlying mathematics of transformations and linear algebra.</li> + <li><a href="https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab" target="_blank">線形代数学の基本</a>: Grant Sandersonによる座標変換と線形代数学に関する入門的なすばらしい動画シリーズ。 +</ul> + +<h2>Exercises</h2> +<h2>演習問題</h2> +<p> + <ul> + <li>Using the last transformation on the container, try switching the order around by first rotating and then translating. See what happens and try to reason why this happens: <a href="/code_viewer_gh.php?code=src/1.getting_started/5.2.transformations_exercise1/transformations_exercise1.cpp" target="_blank">solution</a>.</li> + <li>箱に対して行った最後の変換において、変換の順序を入れ替えて、まず回転しその後平行移動して下さい。なにが起こるのか確認し、その理由を考えて下さい: <a href="/code_viewer_gh.php?code=src/1.getting_started/5.2.transformations_exercise1/transformations_exercise1.cpp" target="_blank">解答</a>。</li> + <li>Try drawing a second container with another call to <fun><function id='2'>glDrawElements</function></fun> but place it at a different position using transformations <strong>only</strong>. Make sure this second container is placed at the top-left of the window and instead of rotating, scale it over time (using the <code>sin</code> function is useful here; note that using <code>sin</code> will cause the object to invert as soon as a negative scale is applied): <a href="/code_viewer_gh.php?code=src/1.getting_started/5.2.transformations_exercise2/transformations_exercise2.cpp" target="_blank">solution</a>.</li> + <li><fun><function id='2'>glDrawElements</function></fun>をもう一度呼び出し、2つ目の箱を描いて下さい。ただし座標変換<strong>のみ</strong>を用いて別の場所に表示して下さい。この2つ目の箱は画面の左上に表示し、今回は回転させるのではなく時間と共に拡大縮小して下さい。拡大縮小には<code>sin</code>関数が便利です。<code>sin</code>を使うと拡大率が負になったときに物体が反転します:<a href="/code_viewer_gh.php?code=src/1.getting_started/5.2.transformations_exercise2/transformations_exercise2.cpp" target="_blank">解答</a>。</li> + </ul> +</p> + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2020/OIT/Introduction.html b/pub/Guest-Articles/2020/OIT/Introduction.html @@ -0,0 +1,486 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Introduction</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2020/OIT/Introduction</h1> +<p> + In the <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">Blending</a> chapter, the subject of color blending was introduced. Blending is the way of implementing transparent surfaces in a 3D scene. In short, transparency delves into the subject of drawing semi-solid or fully see-through objects like glasses in computer graphics. The idea is explained up to a suitable point in that chapter, so if you're unfamiliar with the topic, better read Blending first. + </p> + + <p> + In this article, we are scratching the surface of this topic a bit further, since there are so many techniques involved in implementing such an effect in a 3D environment. + </p> + + <p> + To begin with, we are going to discuss about the limitations of the graphics library/hardware and the hardships they entail, and the reason that why transparency is such a tricky subject. Later on, we will introduce and briefly review some of the more well-known transparency techniques that have been invented and used for the past twenty years associated with the current hardware. Ultimately, we are going to focus on explaining and implementing one of them, which will be the subject of the following part of this article. + </p> + + <p> + Note that the goal of this article is to introduce techniques which have significantly better performance than the technique that was used in the Blending chapter. Otherwise, there isn't a genuinely compelling reason to expand on that matter. + </p> + + <h2>Graphics library/hardware limitations</h2> + + <p> + The reason that this article exists, and you're reading it, is that there is no direct way to draw transparent surfaces with the current technology. Many people wish, that it was as simple as turning on a flag in their graphics API, but that's a fairy tale. Whether, this is a limitation of the graphics libraries or video cards, that's debatable. + </p> + + <p> + As explained in the Blending chapter, the source of this problem arises from combining depth testing and color blending. At the fragment stage, there is no buffer like the depth buffer for transparent pixels that would tell the graphics library, which pixels are fully visible or semi-visible. One of the reasons could be, that there is no efficient way of storing the information of transparent pixels in such a buffer that can hold an infinite number of pixels for each coordinate on the screen. Since each transparent pixel could expose its underlying pixels, therefore there needs to be a way to store different layers of all pixels for all screen coordinates. + </p> + + <p> + This limitation leaves us to think for a way to overcome such an issue and since neither the graphics library nor the hardware gives us a hand, this all has to be done by the developer with the tools at hand. We will examine two methods which are prominent in this subject. One being, <def>ordered transparency</def> and the other <def>order-independent transparency</def>. + </p> + + <h2>Ordered transparency</h2> + + <p> + The most convenient solution to overcome this issue, is to sort your transparent objects, so they're either drawn from the furthest to the nearest, or from the nearest to the furthest in relation to the camera's position. This way, the depth testing wouldn't affect the outcome of those pixels that have been drawn after/before but over/under a further/closer object. However major the expenditure this method entails for the CPU, it was used in many early games that probably most of us have played. + </p> + + <p> + For example, the sample image below shows the importance of blending order. The top part of the image produces an incorrect result with unordered alpha blending, while the bottom correctly sorts the geometry. Note lower visibility of the skeletal structure without correct depth ordering. + This image is from <a href="http://gpuopen.com/archive/gpu-demos/radeon-hd-5000-series-graphics-real-time-demos/" target="_blank">ATI Mecha Demo</a>: + </p> + +<img src="/img/guest/2020/oit/ATI_Mecha_Demo_Screenshot.jpg" width="287" alt="Importance of ordering from ATI Mecha Demo"> + + <p> + So far, we have understood that in order to overcome the limitation of current technology to draw transparent objects, we need order for our transparent objects to be displayed properly on the screen. Ordering takes away performance from your application, and since most of 3D applications are running in real-time, this will be so much more evident as you perform sorting at every frame. + </p> + + <p> + Therefore, we will be looking into the world of order-independent transparency techniques and to find one which better suits our purpose and furthermore our pipeline, so we don't have to sort the objects before drawing. + </p> + + <h2>Order-independent transparency</h2> + + <p> + Order-independent transparency or for short <def>OIT</def>, is a technique which doesn't require us to draw our transparent objects in an orderly fashion. At first glance, this will give us back the CPU cycles that we were taking for sorting the objects, but at the same time OIT techniques have their pros and cons. + </p> + + <p> + The goal of OIT techniques is to eliminate the need of sorting transparent objects at draw time. Depending on the technique, some of them must sort fragments for an accurate result, but only at a later stage when all the draw calls have been made, and some of them don't require sorting, but results are approximated. + </p> + + <h3>History</h3> + + <p> + Some of the more advanced techniques that have been invented to overcome the limitation of rendering transparent surfaces, explicitly use a buffer (e.g. a linked list or a 3D array such as [x][y][z]) that can hold multiple layers of pixels' information and can sort pixels on the GPU, normally because of its parallel processing power, as opposed to CPU. + </p> + + <note> + The <a href="https://en.wikipedia.org/wiki/A-buffer" target="_blank">A-buffer</a> is a computer graphics technique introduced in 1984 which stores per-pixel lists of fragment data (including <a href="https://en.wikipedia.org/wiki/Micropolygon" target="_blank">micro-polygon</a> information) in a software rasterizer, <a href="https://en.wikipedia.org/wiki/Reyes_rendering" target="_blank">REYES</a>, originally designed for anti-aliasing but also supporting transparency. + </note> + + <p> + At the same time, there has been hardware capable of facilitating this task by performing on-hardware calculations which is the most convenient way for a developer to have access to transparency out of the box. + </p> + + <note> + <a href="https://en.wikipedia.org/wiki/Dreamcast#Hardware" target="_blank">SEGA Dreamcast</a> was one of the few consoles that had automatic per-pixel translucency sorting, implemented in its hardware. + </note> + + <p> + Commonly, OIT techniques are separated into two categories which are <def>exact</def> and <def>approximate</def>. Respectively, exact will result in better images with an accurate transparency which suits every scenario, while approximate although resulting in good-looking images, lacks accuracy in complex scenes. + </p> + + <h3>Exact OIT</h3> + + <p> + These techniques accurately compute the final color, for which all fragments must be sorted. For high depth complexity scenes, sorting becomes the bottleneck. + </p> + + <p> + One issue with the sorting stage is <def>local memory limited occupancy</def>, in this case a <a href="https://en.wikipedia.org/wiki/Single_Instruction_Multiple_Threads" target="_blank">single instruction, multiple threads</a> attribute relating to the throughput and operation latency hiding of GPUs. + Although, <a href="http://diglib.eg.org/handle/10.2312/PE.PG.PG2013short.059-064" target="_blank">BMA</a> (backwards memory allocation) can group pixels by their depth complexity and sort them in batches to improve the occupancy and hence performance of low depth complexity pixels in the context of a potentially high depth complexity scene. Up to a 3× overall OIT performance increase is reported. + </p> + + <note> + The sorting stage requires relatively large amounts of temporary memory in shaders that is usually conservatively allocated at a maximum, which impacts memory occupancy and performance. + </note> + + <p> + Sorting is typically performed in a local array, however performance can be improved further by making use of the GPU's memory hierarchy and sorting in registers, similarly to an <a href="https://en.wikipedia.org/wiki/External_sorting#External_merge_sort" target="_blank">external merge sort</a>, especially in conjunction with BMA. + </p> + + <h3>Approximate OIT</h3> + + <p> + Approximate OIT techniques relax the constraint of exact rendering to provide faster results. Higher performance can be gained from not having to store all fragments or only partially sorting the geometry. A number of techniques also compress, or reduce, the fragment data. These include: + </p> + + <ul> + <li>Stochastic Transparency: draw in a higher resolution in full opacity but discard some fragments. Down-sampling will then yield transparency.</li> + <li>Adaptive Transparency: a two-pass technique where the first constructs a visibility function which compresses on the fly (this compression avoids having to fully sort the fragments) and the second uses this data to composite unordered fragments. Intel's pixel synchronization avoids the need to store all fragments, removing the unbounded memory requirement of many other OIT techniques.</li> + </ul> + + <h3>Techniques</h3> + + <p> + Some of the OIT techniques that have been commonly used in the industry are as follows: + </p> + + <ul> + <li><a href="https://en.wikipedia.org/wiki/Depth_peeling" target="_blank">Depth peeling</a>: Introduced in 2001, described a hardware accelerated OIT technique which utilizes the depth buffer to peel a layer of pixels at each pass. With limitations in graphics hardware the scene's geometry had to be rendered many times.</li> + <li><a href="http://developer.download.nvidia.com/SDK/10/opengl/src/dual_depth_peeling/doc/DualDepthPeeling.pdf" target="_blank">Dual depth peeling</a>: Introduced in 2008, improves on the performance of depth peeling, still with many-pass rendering limitation.</li> + <li><a href="http://jcgt.org/published/0002/02/09/" target="_blank">Weighted, blended</a>: Published in 2013, utilizes a weighting function and two buffers for pixel color and pixel reveal threshold for the final composition pass. Results in an approximated image with a decent quality in complex scenes.</li> + </ul> + + <h2>Implementation</h2> + + <p> + The usual way of performing OIT in 3D applications is to do it in multiple passes. There are at least three passes required for an OIT technique to be performed, so in order to do this, you'll have to have a perfect understanding of how <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">Framebuffers</a> work in OpenGL. Once you're comfortable with Framebuffers, it all boils down to the implementation complexity of the technique you are trying to implement. + </p> + + <p> + Briefly explained, the three passes involved are as follows: + </p> + + <ol> + <li>First pass, is where you draw all of your solid objects, this means any object that does not let the light travel through its geometry.</li> + <li>Second pass, is where you draw all of your translucent objects. Objects that need alpha discarding, can be rendered in the first pass.</li> + <li>Third pass, is where you composite the images that resulted from two previous passes and draw that image onto your backbuffer.</li> + </ol> + + <p> + This routine is almost identical in implementing OIT techniques across all different pipelines. + </p> + + <p> + In the next part of this article, we are going to implement weighted, blended OIT which is one of the easiest and high performance OIT techniques that has been used in the video game industry for the past ten years. + </p> + + <h2>Further reading</h2> + <ul> + <li><a href="https://en.wikipedia.org/wiki/Dreamcast#Hardware" target="_blank">SEGA Dreamcast Hardware</a>: Dreamcast was one of the few consoles that had hardware implemented order-independent transparency.</li> + <li><a href="https://en.wikipedia.org/wiki/Order-independent_transparency" target="_blank">Order-independent transparency</a>: A series of techniques that have a great performance and produce nice results even with the approximated methods.</li> + <li><a href="http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html" target="_blank">Weighted, blended order-independent transparency</a>: One of the easiest OIT techniques in terms of implementation while producing highly acceptable images for complex scenes.</li> + </ul> + +<author> + <strong>Article by: </strong>Mahan Heshmati Moghaddam<br/> + <strong>Contact: </strong><a href="mailto:mahangm@gmail.com" target="_blank">e-mail</a> +</author> + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2020/OIT/Weighted-Blended.html b/pub/Guest-Articles/2020/OIT/Weighted-Blended.html @@ -0,0 +1,870 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Weighted Blended</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2020/OIT/Weighted-Blended</h1> + <p> + Weighted, Blended is an approximate order-independent transparency technique which was published in the <a href="http://jcgt.org/published/0002/02/09/">journal of computer graphics techniques</a> in 2013 by Morgan McGuire and Louis Bavoil at NVIDIA to address the transparency problem on a broad class of then gaming platforms. + </p> + + <p> + Their approach to avoid the cost of storing and sorting primitives or fragments is to alter the compositing operator so that it is order independent, thus allowing a pure streaming approach. + </p> + + <p> + Most games have ad-hoc and scene-dependent ways of working around transparent surface rendering limitations. These include limited sorting, additive-only blending, and hard-coded render and composite ordering. Most of these methods also break at some point during gameplay and create visual artifacts. One not-viable alternative is <a href="http://developer.download.nvidia.com/SDK/10/opengl/screenshots/samples/dual_depth_peeling.html" target="_blank">depth peeling</a>, which produces good images, but is too slow for scenes with many layers both in theory and practice. + </p> + + <p> + There are many <a href="https://en.wikipedia.org/wiki/Asymptotic_analysis" target="_blank">asymptotically</a> fast solutions for transparency rendering, such as bounded A-buffer approximations using programmable blending (e.g., <a href="http://software.intel.com/en-us/articles/multi-layer-alpha-blending">Marco Salvi's work</a>), stochastic transparency (as <a href="https://www.computer.org/csdl/journal/tg/2011/08/ttg2011081036/13rRUxBa55X" target="_blank">explained by Eric Enderton and others</a>), and ray tracing. One or more of these will probably dominate at some point, but all were impractical on the game platforms of five or six years ago, including PC DX11/GL4 GPUs, mobile devices with OpenGL ES 3.0 GPUs, and last-generation consoles like PlayStation 4. + </p> + + <note> + In mathematical analysis, asymptotic analysis, also known as asymptotics, is a method of describing limiting behavior. + </note> + + <p> + The below image is a transparent CAD view of a car engine rendered by this technique. + </p> + +<img src="/img/guest/2020/oit/cad_view_of_an_engine.png" width="560" alt="A transparent CAD view of a car engine rendered by this technique."> + + <h2>Theory</h2> + + <p> + This technique renders non-refractive, monochrome transmission through surfaces that themselves have color, without requiring sorting or new hardware features. In fact, it can be implemented with a simple shader for any GPU that supports blending to render targets with more than 8 bits per channel. + </p> + + <p> + It works best on GPUs with multiple render targets and floating-point texture, where it is faster than sorted transparency and avoids sorting artifacts and popping for particle systems. It also consumes less bandwidth than even a 4-deep RGBA8 K-buffer and allows mixing low-resolution particles with full-resolution surfaces such as glass. + </p> + + <p> + For the mixed resolution case, the peak memory cost remains that of the higher resolution render target but bandwidth cost falls based on the proportional of low-resolution surfaces. + </p> + + <p> + The basic idea of Weighted, Blended method is to compute the coverage of the background by transparent surfaces exactly, but to only approximate the light scattered towards the camera by the transparent surfaces themselves. The algorithm imposes a heuristic on inter-occlusion factor among transparent surfaces that increases with distance from the camera. + </p> + + <note> + A heuristic technique, or a heuristic, is any approach to problem solving or self-discovery that employs a practical method that is not guaranteed to be optimal, perfect, or rational, but is nevertheless sufficient for reaching an immediate, short-term goal or approximation. In our case, the heuristic is the weighting function. + </note> + + <p> + After all transparent surfaces have been rendered, it then performs a full-screen normalization and compositing pass to reduce errors where the heuristic was a poor approximation of the true inter-occlusion. + </p> + + <p> + The below image is a glass chess set rendered with this technique. Note that the glass pieces are not refracting any light. + </p> + + <img src="/img/guest/2020/oit/a_glass_chess_set.png" width="560" alt="A glass chess set rendered with this technique."> + + <p> + For a better understanding and a more detailed explanation of the weight function, please refer to page 5, 6 and 7 of the original paper as the Blended OIT has been implemented and improved by different methods along the years. Link to the paper is provided at the end of this article. + </p> + + <h2>Limitation</h2> + + <p> + The primary limitation of the technique is that the weighting heuristic must be tuned for the anticipated depth range and opacity of transparent surfaces. + </p> + + <p> + The technique was implemented in OpenGL for the <a href="http://g3d.sf.net/">G3D Innovation Engine</a> and DirectX for the <a href="http://www.unrealengine.com/">Unreal Engine</a> to produce the results live and in the paper. Dan Bagnell and Patrick Cozzi <a href="http://bagnell.github.io/cesium/Apps/Sandcastle/gallery/OIT.html">implemented it in WebGL</a> for their open-source Cesium engine (see also their <a href="http://cesiumjs.org/2014/03/14/Weighted-Blended-Order-Independent-Transparency/">blog post</a> discussing it). + </p> + + <p> + From those implementations, a good set of weighting functions were found, which are reported in the journal paper. In the paper, they also discuss how to spot artifacts from a poorly-tuned weighting function and fix them. + </p> + + <p> + Also, I haven't been able to find a proper way to implement this technique in a deferred renderer. Since pixels override each other in a deferred renderer, we lose information about the previous layers so we cannot correctly accumulate the color values for the lighting stage. + </p> + + <p> + One feasible solution is to apply this technique as you would ordinarily do in a forward renderer. This is basically borrowing the transparency pass of a forward renderer and incorporate it in a deferred one. + </p> + + <h2>Implementation</h2> + + <p> + This technique is fairly straight forward to implement and the shader modifications are very simple. If you're familiar with how Framebuffers work in OpenGL, you're almost halfway there. + </p> + + <p> + The only caveat is we need to write our code in OpenGL ^4.0 to be able to use blending to multiple render targets (e.g. utilizing <fun><function id='70'>glBlendFunc</function>i</fun>). In the paper, different ways of implementation have been discussed for libraries that do not support rendering or blending to multiple targets. + </p> + + <warning>Don't forget to change your OpenGL version when initializng GLFW and also your GLSL version in your shaders.</warning> + + <h3>Overview</h3> + + <p> + During the transparent surface rendering, shade surfaces as usual, but output to two render targets. The first render target (<def>accum</def>) must have at least <var>RGBA16F</var> precision and the second (<def>revealage</def>) must have at least <var>R8</var> precision. Clear the first render target to <var>vec4(0)</var> and the second render target to 1 (using a pixel shader or <fun><function id='10'>glClear</function>Buffer</fun> + <fun><function id='10'>glClear</function></fun>). + </p> + + <p> + Then, render the surfaces in any order to these render targets, adding the following to the bottom of the pixel shader and using the specified blending modes: + </p> + +<pre><code> +// your first render target which is used to accumulate pre-multiplied color values +layout (location = 0) out vec4 accum; + +// your second render target which is used to store pixel revealage +layout (location = 1) out float reveal; + +... + +// output linear (not gamma encoded!), unmultiplied color from the rest of the shader +vec4 color = ... // regular shading code + +// insert your favorite weighting function here. the color-based factor +// avoids color pollution from the edges of wispy clouds. the z-based +// factor gives precedence to nearer surfaces +float weight = + max(min(1.0, max(max(color.r, color.g), color.b) * color.a), color.a) * + clamp(0.03 / (1e-5 + pow(z / 200, 4.0)), 1e-2, 3e3); + +// blend func: GL_ONE, GL_ONE +// switch to pre-multiplied alpha and weight +accum = vec4(color.rgb * color.a, color.a) * weight; + +// blend func: GL_ZERO, GL_ONE_MINUS_SRC_ALPHA +reveal = color.a; +</code></pre> + + <p> + Finally, after all surfaces have been rendered, composite the result onto the screen using a full-screen pass: + </p> + +<pre><code> +// bind your accum render target to this texture unit +layout (binding = 0) uniform sampler2D rt0; + +// bind your reveal render target to this texture unit +layout (binding = 1) uniform sampler2D rt1; + +// shader output +out vec4 color; + +// fetch pixel information +vec4 accum = texelFetch(rt0, int2(gl_FragCoord.xy), 0); +float reveal = texelFetch(rt1, int2(gl_FragCoord.xy), 0).r; + +// blend func: GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA +color = vec4(accum.rgb / max(accum.a, 1e-5), reveal); +</code></pre> + + <p> + Use this table as a reference for your render targets: + </p> + + <table border="1"> + <tbody> + <tr><td>Render Target</td><td>Format</td><td>Clear</td><td>Src Blend</td><td>Dst Blend</td><td>Write ("Src")</td></tr> + <tr><td>accum</td><td>RGBA16F</td><td>(0,0,0,0)</td><td>ONE</td><td>ONE</td><td><code>(r*a, g*a, b*a, a) * w</code></td></tr> + <tr><td>revealage</td><td>R8</td><td>(1,0,0,0)</td><td>ZERO</td><td>ONE_MINUS_SRC_COLOR</td><td><code>a</code></td></tr> + </tbody> + </table> + + <p> + A total of three rendering passes are needed to accomplish the finished result which is down below: + </p> + + <img src="/img/guest/2020/oit/weighted_blended_result.png" width="640" alt="Weighted, Blended result."> + + <h3>Details</h3> + + <p> + To get started, we would have to setup a quad for our solid and transparent surfaces. The red quad will be the solid one, and the green and blue will be the transparent one. Since we're using the same quad for our screen quad as well, here we define UV values for texture mapping purposes at the screen pass. + </p> + +<pre><code> +float quadVertices[] = { + // positions // uv + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f +}; + +// quad VAO +unsigned int quadVAO, quadVBO; +<function id='33'>glGenVertexArrays</function>(1, &quadVAO); +<function id='12'>glGenBuffers</function>(1, &quadVBO); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, quadVBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(1); +<function id='30'>glVertexAttribPointer</function>(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); +<function id='27'>glBindVertexArray</function>(0); +</code></pre> + + <p> + Next, we will create two framebuffers for our solid and transparent passes. Our solid pass needs a color buffer and a depth buffer to store color and depth information. Our transparent pass needs two color buffers to store color accumulation and pixel revealage threshold. We will also attach the opaque framebuffer's depth texture to our transparent framebuffer, to utilize it for depth testing when rendering our transparent surfaces. + </p> + +<pre><code> +// set up framebuffers +unsigned int opaqueFBO, transparentFBO; +<function id='76'>glGenFramebuffers</function>(1, &opaqueFBO); +<function id='76'>glGenFramebuffers</function>(1, &transparentFBO); + +// set up attachments for opaque framebuffer +unsigned int opaqueTexture; +<function id='50'>glGenTextures</function>(1, &opaqueTexture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, opaqueTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); + +unsigned int depthTexture; +<function id='50'>glGenTextures</function>(1, &depthTexture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, depthTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT, + 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); + +... + +// set up attachments for transparent framebuffer +unsigned int accumTexture; +<function id='50'>glGenTextures</function>(1, &accumTexture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, accumTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); + +unsigned int revealTexture; +<function id='50'>glGenTextures</function>(1, &revealTexture); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, revealTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_R8, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); + +... + +// don't forget to explicitly tell OpenGL that your transparent framebuffer has two draw buffers +const GLenum transparentDrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; +glDrawBuffers(2, transparentDrawBuffers); +</code></pre> + + <note> + For the sake of this article, we are creating two separate framebuffers, so it would be easier to understand how the technique unfolds. We could omit the opaque framebuffer and use backbuffer for our solid pass or just create a single framebuffer with four attachments all together (opaque, accumulation, revealage, depth) and render to different render targets at each pass. + </note> + + <p> + Before rendering, setup some model matrices for your quads. You can set the Z axis however you want since this is an order-independent technique and objects closer or further to the camera would not impose any problem. + </p> + +<pre><code>glm::mat4 redModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 0.0f)); +glm::mat4 greenModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 1.0f)); +glm::mat4 blueModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 2.0f)); +</code></pre> + + <p> + At this point, we have to perform our solid pass, so configure the render states and bind the opaque framebuffer. + </p> + +<pre><code> +// configure render states +<function id='60'>glEnable</function>(GL_DEPTH_TEST); +<function id='66'>glDepthFunc</function>(GL_LESS); +<function id='65'>glDepthMask</function>(GL_TRUE); +glDisable(GL_BLEND); +<function id='13'><function id='10'>glClear</function>Color</function>(0.0f, 0.0f, 0.0f, 0.0f); + +// bind opaque framebuffer to render solid objects +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, opaqueFBO); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +</code></pre> + + <p> + We have to reset our depth function and depth mask for the solid pass at every frame since pipeline changes these states further down the line. + </p> + + <p> + Now, draw the solid objects using the solid shader. You can draw alpha cutout objects both at this stage and the next stage as well. The solid shader is just a simple shader that transforms the vertices and draws the mesh with the supplied color. + </p> + +<pre><code> +// use solid shader +solidShader.use(); + +// draw red quad +solidShader.setMat4("mvp", vp * redModelMat); +solidShader.setVec3("color", glm::vec3(1.0f, 0.0f, 0.0f)); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +</code></pre> + + <p> + So far so good. For our transparent pass, like in the solid pass, configure the render states to blend to these render targets as below, then bind the transparent framebuffer and clear its two color buffers to <var>vec4(0.0f)</var> and <var>vec4(1.0)</var>. + </p> + +<pre><code> +// configure render states +// disable depth writes so transparent objects wouldn't interfere with solid pass depth values +<function id='65'>glDepthMask</function>(GL_FALSE); +<function id='60'>glEnable</function>(GL_BLEND); +<function id='70'>glBlendFunc</function>i(0, GL_ONE, GL_ONE); // accumulation blend target +<function id='70'>glBlendFunc</function>i(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); // revealge blend target +<function id='72'>glBlendEquation</function>(GL_FUNC_ADD); + +// bind transparent framebuffer to render transparent objects +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, transparentFBO); +// use a four component float array or a glm::vec4(0.0) +<function id='10'>glClear</function>Bufferfv(GL_COLOR, 0, &zeroFillerVec[0]); + // use a four component float array or a glm::vec4(1.0) +<function id='10'>glClear</function>Bufferfv(GL_COLOR, 1, &oneFillerVec[0]); +</code></pre> + + <p> + Then, draw the transparent surfaces with your preferred alpha values. + </p> + +<pre><code> +// use transparent shader +transparentShader.use(); + +// draw green quad +transparentShader.setMat4("mvp", vp * greenModelMat); +transparentShader.setVec4("color", glm::vec4(0.0f, 1.0f, 0.0f, 0.5f)); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); + +// draw blue quad +transparentShader.setMat4("mvp", vp * blueModelMat); +transparentShader.setVec4("color", glm::vec4(0.0f, 0.0f, 1.0f, 0.5f)); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +</code></pre> + + <p> + The transparent shader is where half the work is done. It's primarily a shader that collects pixel information for our composite pass: + </p> + +<pre><code> +// shader outputs +layout (location = 0) out vec4 accum; +layout (location = 1) out float reveal; + +// material color +uniform vec4 color; + +void main() +{ + // weight function + float weight = clamp(pow(min(1.0, color.a * 10.0) + 0.01, 3.0) * 1e8 * + pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); + + // store pixel color accumulation + accum = vec4(color.rgb * color.a, color.a) * weight; + + // store pixel revealage threshold + reveal = color.a; +} +</code></pre> + + <p> + Note that, we are directly using the color passed to the shader as our final fragment color. Normally, if you are in a lighting shader, you want to use the final result of the lighting to store in accumulation and revealage render targets. + </p> + + <p> + Now that everything has been rendered, we have to <def>composite</def> these two images so we can have the finished result. + </p> + + <note> + Compositing is a common method in many techniques that use a post-processing quad drawn all over the screen. Think of it as merging two layers in a photo editing software like Photoshop or Gimp. + </note> + + <p> + In OpenGL, we can achieve this by color blending feature: + </p> + +<pre><code> +// set render states +<function id='66'>glDepthFunc</function>(GL_ALWAYS); +<function id='60'>glEnable</function>(GL_BLEND); +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +// bind opaque framebuffer +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, opaqueFBO); + +// use composite shader +compositeShader.use(); + +// draw screen quad +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, accumTexture); +<function id='49'>glActiveTexture</function>(GL_TEXTURE1); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, revealTexture); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +</code></pre> + + <p> + Composite shader is where the other half of the work is done. We're basically merging two layers, one being the solid objects image and the other being the transparent objects image. Accumulation buffer tells us about the color and revealage buffer determines the visibility of the the underlying pixel: + </p> + +<pre><code> +// shader outputs +layout (location = 0) out vec4 frag; + +// color accumulation buffer +layout (binding = 0) uniform sampler2D accum; + +// revealage threshold buffer +layout (binding = 1) uniform sampler2D reveal; + +// epsilon number +const float EPSILON = 0.00001f; + +// calculate floating point numbers equality accurately +bool isApproximatelyEqual(float a, float b) +{ + return abs(a - b) &lt;= (abs(a) &lt; abs(b) ? abs(b) : abs(a)) * EPSILON; +} + +// get the max value between three values +float max3(vec3 v) +{ + return max(max(v.x, v.y), v.z); +} + +void main() +{ + // fragment coordination + ivec2 coords = ivec2(gl_FragCoord.xy); + + // fragment revealage + float revealage = texelFetch(reveal, coords, 0).r; + + // save the blending and color texture fetch cost if there is not a transparent fragment + if (isApproximatelyEqual(revealage, 1.0f)) + discard; + + // fragment color + vec4 accumulation = texelFetch(accum, coords, 0); + + // suppress overflow + if (isinf(max3(abs(accumulation.rgb)))) + accumulation.rgb = vec3(accumulation.a); + + // prevent floating point precision bug + vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON); + + // blend pixels + frag = vec4(average_color, 1.0f - revealage); +} +</code></pre> + + <p> + Note that, we are using some helper functions like <fun>isApproximatelyEqual</fun> or <fun>max3</fun> to help us with the accurate calculation of floating-point numbers. Due to inaccuracy of floating-point numbers calculation in current generation processors, we need to compare our values with an extremely small amount called an <def>epsilon</def> to avoid underflows or overflows. + </p> + + <p> + Also, we don't need an intermediate framebuffer to do compositing. We can use our opaque framebuffer as the base framebuffer and paint over it since it already has the opaque pass information. Plus, we're stating that all depth tests should pass since we want to paint over the opaque image. + </p> + + <p> + Finally, draw your composited image (which is the opaque texture attachment since you rendered your transparent image over it in the last pass) onto the backbuffer and observe the result. + </p> + +<pre><code> +// set render states +glDisable(GL_DEPTH); +<function id='65'>glDepthMask</function>(GL_TRUE); // enable depth writes so <function id='10'>glClear</function> won't ignore clearing the depth buffer +glDisable(GL_BLEND); + +// bind backbuffer +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +<function id='13'><function id='10'>glClear</function>Color</function>(0.0f, 0.0f, 0.0f, 0.0f); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + +// use screen shader +screenShader.use(); + +// draw final screen quad +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, opaqueTexture); +<function id='27'>glBindVertexArray</function>(quadVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); +</code></pre> + + <p> + Screen shader is just a simple post-processing shader which draws a full-screen quad. + </p> + + <p> + In a regular pipeline, you would also apply gamma-correction, tone-mapping, etc. in an intermediate post-processing framebuffer before you render to backbuffer, but ensure you are not applying them while rendering your solid and transparent surfaces and also not before composition since this transparency technique needs raw color values for calculating transparent pixels. + </p> + + <p> + Now, the interesting part is to play with the Z axis of your objects to see order-independence in action. Try to place your transparent objects behind the solid object or mess up the orders entirely. + </p> + + <img src="/img/guest/2020/oit/weighted_blended_reordered.png" width="640" alt="Weighted, Blended reordered."> + + <p> + In the image above, the green quad is rendered after the red quad, but behind it, and if you move the camera around to see the green quad from behind, you won't see any artifacts. + </p> + + <p> + As stated earlier, one limitation that this technique imposes is that for scenes with higher depth/alpha complexity we need to tune the weighting function to achieve the correct result. Luckily, a number of tested weighting functions are provided in the paper which you can refer and investigate them for your environment. + </p> + + <p> + Be sure to also check the colored transmission transparency which is the improved version of this technique in the links below. + </p> + + <p> + You can find the source code for this demo <a href="/code_viewer_gh.php?code=src/8.guest/2020/oit/weighted_blended.cpp" target="_blank">here</a>. + </p> + + <h2>Further reading</h2> + + <ul> + <li><a href="http://jcgt.org/published/0002/02/09" href="_blank">Weighted, Blended paper</a>: The original paper published in the journal of computer graphics. A brief history of the transparency and the emergence of the technique itself is provided. This is a must for the dedicated readers.</li> + <li><a href="http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html" href="_blank">Weighted, Blended introduction</a>: Casual Effects is Morgan McGuire's personal blog. This post is the introduction of their technique which goes into further details and is definitely worth to read. Plus, there are videos of their implementation live in action that you would not want to miss.</li> + <li><a href="http://casual-effects.blogspot.com/2015/03/implemented-weighted-blended-order.html" href="_blank">Weighted, Blended for implementors</a>: And also another blog post by him on implementing the technique for implementors.</li> + <li><a href="http://casual-effects.blogspot.com/2015/03/colored-blended-order-independent.html" href="_blank">Weighted, Blended and colored transmission</a>: And another blog post on colored transmission for transparent surfaces.</li> + <li><a href="http://bagnell.github.io/cesium/Apps/Sandcastle/gallery/OIT.html" href="_blank">A live implementation of the technique</a>: This is a live WebGL visualization from Cesium engine which accepts weighting functions for you to test in your browser!</li> + </ul> + +<author> + <strong>Article by: </strong>Mahan Heshmati Moghaddam<br/> + <strong>Contact: </strong><a href="mailto:mahangm@gmail.com" target="_blank">e-mail</a> +</author> + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2020/Skeletal-Animation.html b/pub/Guest-Articles/2020/Skeletal-Animation.html @@ -0,0 +1,1207 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Skeletal Animation</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2020/Skeletal-Animation</h1> +<p>3D Animations can bring our games to life. Objects in 3D world like humans and &amp; + animals feel more organic when they move their limbs to do certain things like walking, running &amp; attacking. + This tutorial is about Skeletal animation which you all have been waiting for. We will first understand the concept thoroughly and then understand the data + we need to animate a 3D model using Assimp. I'd recommend you to finish the <a href="https://learnopengl.com/Model-Loading/Assimp">Model Loading</a> chapter of this saga as this tutorial code continues from there. You can still understand the concept and implement it in your way. So let's get started.</p> + + + <h3>Interpolation</h3> +<p>To understand how animation works at basic level we need to understand the concept of Interpolation. + Interpolation can be defined as something happening over time. Like an enemy moving from point A to point B in time T i.e Translation happening over time . + A gun turret smoothly rotates to face the target i.e Rotation happening over time and a tree is scaling up from size A to size B in time T i.e Scaling happening over time.</p> +<p>A simple interpolation equation used for Translation and Scale looks like this..</p> +<p style="text-align: center;"><strong>a = a * (1 - t) + b * t </strong></p> +<p>It is known as as Linear Interpolation equation or Lerp. For Rotation we cannot use Vector. + The reason for that is if we went ahead and tried to use the linear interpolation equation on a vector of X(Pitch),Y(Yaw) & Z(Roll), the interpolation won't be linear. + You will encounter + weird issues like The + Gimbal Lock(See references section below to learn about it). To avoid this issue we use Quaternion for rotations. + Quaternion provides something called The Spherical Interpolation or + Slerp equation which gives the same result as Lerp but for two rotations A & B. + I won't be able to explain how the equation works because its out of the scope for now. You can surely checkout references section below to + understand The Quaternion. +</p> +<h3>Components of An Animated Model : Skin, Bones and Keyframes</h3> +<p>The whole process of an animation starts with the addition of the first component which is The Skin in a software like blender or Maya. + Skin is nothing but meshes which add visual aspect to the model to tell the viewer how it looks like. + But If you want to move any mesh then just like the real world, you need to add Bones. You can see the images below to understand how it looks in software like blender....</p> +<p>&nbsp;</p> +<img src="/img/guest/2020/skeletal_animation/skin.png" alt="skin" width="300" height="300" class="clean"> <img src="/img/guest/2020/skeletal_animation/bones.png" alt="bones" width="300" height="300"class="clean"> <img src="/img/guest/2020/skeletal_animation/merged.png" alt="skin and bones" width="300" height="300"class="clean"> +<p>These bones are usually added in hierarchical fashion for characters like humans &amp; animals and the reason is pretty obvious. We want parent-child relationship among limbs. + For example, If we move our right shoulder then our right bicep, forearm, hand and fingers should move as well. This is how the hierarchy looks like....</p> +<p>&nbsp;</p> + + <p> + <img src="/img/guest/2020/skeletal_animation/parent_child.png" alt="" width="853" height="425"/></p> + +<p>In the above diagram if you grab the hip bone and move it, all limbs will be affected by its movement.</p> +<p>At this point, we are ready to create KeyFrames for an animation. Keyframes are poses at different point of time in an animation. We will interpolate between + these Keyframes to go from one pose to another pose smoothly in our code. Below you can see how poses are created for a simple 4 frame jump animation...</p> + <p><img src="/img/guest/2020/skeletal_animation/poses.gif" width="300px" class="clean" alt=""/> <img src="/img/guest/2020/skeletal_animation/interpolating.gif" class="clean" width="300px" alt="Interpolation bw frames" hspace="20"/></p> +<p>&nbsp;</p> +<h3>How Assimp holds animation data</h3> +<p>We are almost there to the code part but first we need to understand how assimp holds imported animation data. Look at the diagram below..</p> + <p><img src="/img/guest/2020/skeletal_animation/assimp1.jpeg" alt="" width="710" height="800"/></p> +<p>Just like in the <a href="https://learnopengl.com/Model-Loading/Assimp">Model Loading</a> chapter, we will start with the <code>aiScene</code> pointer + which holds a pointer to the root node and look what do we have here, an array of Animations. + This array of <code>aiAnimation</code> contains the general information like duration of an animation represented here as + <code>mDuration</code> and then we have a <code>mTicksPerSecond</code> variable, which controls how fast + we should interpolate between frames. If you remember from the last section that an animation has keyframes. + Similary, an <code>aiAnimation</code> contains an <code>aiNodeAnim</code> array called Channels. + This array of contains all bones and their keyframes which are going to be engaged in an animation. + + An <code>aiNodeAnim</code> contains name of the bone and you + will find 3 types of keys to interpolate between here, Translation,Rotation &amp; Scale.</p> + +<p>Alright, there's one last thing we need to understand and we are good to go for writing some code.</p> + +<p>&nbsp;</p> +<h3>Influence of multiple bones on vertices</h3> +<p>When we curl our forearm and we see our biceps muscle pop up. We can also say that forearm bone transformation is affecting vertices on our biceps. + Similary, there could be multiple bones affecting a single vertex in a mesh. + For characters like solid metal robots all forearm vertices will only be affected by forearm bone but for characters like humans, animals etc, there could be + upto 4 bones which can affect a vertex.&nbsp; Let's see how assimp stores that information...</p> +<p>&nbsp;</p> + <p><img src="/img/guest/2020/skeletal_animation/assimp2.jpeg" alt="" width="760" height="860"/></p> +<p>&nbsp;</p> +<p>We start with the <code>aiScene</code> pointer again which contains an array of all aiMeshes. + Each <code>aiMesh</code> object has an array of <code>aiBone</code> which contains the information like + how much influence this <code>aiBone</code> will have on set of vertices on the mesh. + aiBone contains the name of the bone, an array of <code>aiVertexWeight</code> which basically + tells us how much influence this <code>aiBone</code> will have on what vertices on the mesh. + Now we have one more member of <code>aiBone</code> which is offsetMatrix. It's a 4x4 matrix + used to transform vertices from model space to their bone space. + You can see this in action in images below....</p> + + <img src="/img/guest/2020/skeletal_animation/mesh_space.png" class="clean" alt="Mesh Space" style="width:50%"> + <img src="/img/guest/2020/skeletal_animation/bone_space.png" class="clean" alt="Bone Space" style="width:50%"> + <p> + When vertices are in bone space they will be transformed relative to their bone + as they are supposed to. You will soon see this in action + in code. + </p> + +<h3>Finally! Let's code.</h3> +<p>Thank you for making it this far. We will start with directly looking at the end result which is our final vertex + shader code. This will give us good sense what we need at the end.. </p> + +<pre><code>#version 430 core + +layout(location = 0) in vec3 pos; +layout(location = 1) in vec3 norm; +layout(location = 2) in vec2 tex; +layout(location = 3) in ivec4 boneIds; +layout(location = 4) in vec4 weights; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +const int MAX_BONES = 100; +const int MAX_BONE_INFLUENCE = 4; +uniform mat4 finalBonesMatrices[MAX_BONES]; + +out vec2 TexCoords; + +void main() +{ + vec4 totalPosition = vec4(0.0f); + for(int i = 0 ; i &lt; MAX_BONE_INFLUENCE ; i++) + { + if(boneIds[i] == -1) + continue; + if(boneIds[i] &gt;= MAX_JOINTS) + { + totalPosition = vec4(pos,1.0f); + break; + } + vec4 localPosition = finalBoneMatrices[boneIds[i]] * vec4(pos,1.0f); + totalPosition += localPosition * weights[i]; + vec3 localNormal = mat3(finalBoneMatrices[boneIds[i]]) * norm; + } + + mat4 viewModel = view * model; + gl_Position = projection * viewModel * totalPosition; + TexCoords = tex; +} +</code></pre> + +<p> + Fragment shader remains the same from the <a href="https://learnopengl.com/Model-Loading/Model">model loading</a> chapter. + Starting from the top you see two new attributes layout declaration. + First <code>boneIds</code> and second is <code>weights</code>. we also have + a uniform array <code>finalBonesMatrices</code> which stores transformations of all bones. + &nbsp;&nbsp;<code>boneIds</code> contains indices which are used to read the <code>finalBonesMatrices</code> + array and apply those transformation to <code>pos</code> vertex with their respective weights + stored in <code> weights </code> array. This happens inside <code> for </code> loop above. + Now let's add support in our <code>Mesh</code> class for bone weights first.. +</p> + +<pre><code>#define MAX_BONE_INFLUENCE 4 + +struct Vertex { + // position + glm::vec3 Position; + // normal + glm::vec3 Normal; + // texCoords + glm::vec2 TexCoords; + + //bone indexes which will influence this vertex + int m_BoneIDs[MAX_BONE_INFLUENCE]; + + //weights from each bone + float m_Weights[MAX_BONE_INFLUENCE]; +}; +</code></pre> + +<p> + We have added two new attributes for the <code>Vertex</code>, just like we saw in our vertex shader. + Now's let's load them in GPU buffers just like other attributes in our <code>Mesh::setupMesh </code> function... + </p> + +<pre><code>class Mesh +{ + ... + + void setupMesh() + { + ... + + // ids + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(3); + glVertexAttribIPointer(3, 4, GL_INT, sizeof(Vertex), + (void*)offsetof(Vertex, m_BoneIDs)); + + // weights + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(4); + <function id='30'>glVertexAttribPointer</function>(4, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), + (void*)offsetof(Vertex, m_Weights)); + + ... + } + ... +} +</code></pre> +<p>Just like before, except now we have added 3 and 4 layout location ids for <code>boneIds</code> and <code>weights</code>. One imporant thing to notice here is how we are passing data for <code>boneIds</code>. We are using <code>glVertexAttribIPointer</code> and we passed GL_INT as third parameter.&nbsp;</p> +<p>Now we can extract the bone-weight information from the assimp data structure. Let's make some changes in Model class...</p> + +<pre><code>struct BoneInfo +{ + /*id is index in finalBoneMatrices*/ + int id; + + /*offset matrix transforms vertex from model space to bone space*/ + glm::mat4 offset; + +}; +</code></pre> + +<p> This <code> BoneInfo </code> will store our offset matrix and also a unique id which will + be used as an index to store it in <code>finalBoneMatrices</code> array we saw earlier in our shader. +Now we will add bone weight extraction support in <code>Model</code>... </p> + +<pre><code>class Model +{ +private: + ... + std::map&lt;string, BoneInfo&gt; m_BoneInfoMap; // + int m_BoneCounter = 0; + + ... + void SetVertexBoneDataToDefault(Vertex&amp; vertex) + { + for (int i = 0; i &lt; MAX_BONE_WEIGHTS; i++) + { + vertex.m_BoneIDs[i] = -1; + vertex.m_Weights[i] = 0.0f; + } + } + + Mesh processMesh(aiMesh* mesh, const aiScene* scene) + { + vector vertices; + vector indices; + vector textures; + + for (unsigned int i = 0; i &lt; mesh-&gt;mNumVertices; i++) + { + Vertex vertex; + + SetVertexBoneDataToDefault(vertex); + + vertex.Position = AssimpGLMHelpers::GetGLMVec(mesh-&gt;mVertices[i]); + vertex.Normal = AssimpGLMHelpers::GetGLMVec(mesh-&gt;mNormals[i]); + + if (mesh-&gt;mTextureCoords[0]) + { + glm::vec2 vec; + vec.x = mesh-&gt;mTextureCoords[0][i].x; + vec.y = mesh-&gt;mTextureCoords[0][i].y; + vertex.TexCoords = vec; + } + else + vertex.TexCoords = glm::vec2(0.0f, 0.0f); + + vertices.push_back(vertex); + } + ... + ExtractBoneWeightForVertices(vertices,mesh,scene); + + return Mesh(vertices, indices, textures); + } + + void SetVertexBoneData(Vertex&amp; vertex, int boneID, float weight) + { + for (int i = 0; i &lt; MAX_BONE_WEIGHTS; ++i) + { + if (vertex.m_BoneIDs[i] &lt; 0) + { + vertex.m_Weights[i] = weight; + vertex.m_BoneIDs[i] = boneID; + break; + } + } + } + + void ExtractBoneWeightForVertices(std::vector&amp; vertices, aiMesh* mesh, + const aiScene* scene) + { + for (int boneIndex = 0; boneIndex &lt; mesh-&gt;mNumBones; ++boneIndex) + { + int boneID = -1; + std::string boneName = mesh-&gt;mBones[boneIndex]-&gt;mName.C_Str(); + if (m_BoneInfoMap.find(boneName) == m_BoneInfoMap.end()) + { + BoneInfo newBoneInfo; + newBoneInfo.id = m_BoneCounter; + newBoneInfo.offset = AssimpGLMHelpers:: + ConvertMatrixToGLMFormat(mesh-&gt;mBones[boneIndex]-&gt;mOffsetMatrix); + m_BoneInfoMap[boneName] = newBoneInfo; + boneID = m_BoneCounter; + m_BoneCounter++; + } + else + { + boneID = m_BoneInfoMap[boneName].id; + } + assert(boneID != -1); + auto weights = mesh-&gt;mBones[boneIndex]-&gt;mWeights; + int numWeights = mesh-&gt;mBones[boneIndex]-&gt;mNumWeights; + + for (int weightIndex = 0; weightIndex &lt; numWeights; ++weightIndex) + { + int vertexId = weights[weightIndex].mVertexId; + float weight = weights[weightIndex].mWeight; + assert(vertexId &lt;= vertices.size()); + SetVertexBoneData(vertices[vertexId], boneID, weight); + } + } + } + ....... +}; +</code></pre> + +<p>We start by declaring a map <code>m_BoneInfoMap</code> and a counter <code>m_BoneCounter</code> + which will be incremented as soon as we read a new bone. + we saw in the diagram earlier that each <code>aiMesh</code> contains all + aiBones which are associated with the <code>aiMesh</code>. + The whole process of the bone-weight extraction starts from the + <code> processMesh </code> + function. For each loop iteration we are setting <code>m_BoneIDs</code> and <code>m_Weights</code> to + their default values + by calling function <code>SetVertexBoneDataToDefault</code>. + Just before the <code>processMesh</code> function ends, we call the + <code>ExtractBoneWeightData</code>. In the <code>ExtractBoneWeightData</code> we run + a for loop for each <code>aiBone</code> and check if this bone already exists in the <code>m_BoneInfoMap</code>. + If we couldn't find it then it's considered a new bone and we create new <code>BoneInfo</code> + with an id and store its associated <code>mOffsetMatrix</code> to it. Then we store this new <code>BoneInfo</code> + in <code>m_BoneInfoMap</code> and then we increment the <code>m_BoneCounter</code> counter to create + an id for next bone. In case we find the bone name in <code>m_BoneInfoMap</code> then + that means this bone affects vertices of mesh out of + its scope. So we take it's Id and proceed further to know which vertices it affects. </p> + + <p> One thing to notice that we are calling <code>AssimpGLMHelpers::ConvertMatrixToGLMFormat</code>. + Assimp store its matrix data in different format than GLM so this function just gives us our matrix in GLM format. + </p> + <p>We have extracted the offsetMatrix for the bone and now we will simply iterate its <code>aiVertexWeight</code>array + and extract all vertices indices which will be influenced by this bone along with their + respective weights and call <code>SetVertexBoneData</code> to fill up <code>Vertex.boneIds</code> and <code>Vertex.weights</code> with extracted information. </p> + + <p>Phew! You deserve a coffee break at this point. </p> + +<h3>Bone,Animation &amp; Animator classes</h3> +<p>Here's high level view of classes..</p> + + <p><img src="/img/guest/2020/skeletal_animation/bird_eye_view.png" class="clean" alt="" width="700"/></p> + +<p> Let us remind ourselves what we are trying to achieve. For each rendering frame we want to interpolate all bones in heirarchy smoothly and get their final transformations matrices which will be supplied to shader + uniform <code>finalBonesMatrices</code>. + Here's what each class does... + + <p><b>Bone</b> : A single bone which reads all keyframes data from <code>aiNodeAnim</code>. It will also interpolate between its keys i.e Translation,Scale & Rotation based on the current animation time. </p> + <p><b>AssimpNodeData</b> : This struct will help us to isolate our <code><b>Animation</b> from Assimp. </code> </p> + <p><b>Animation</b> : An asset which reads data from aiAnimation and create a heirarchical record of <code><b>Bone</b></code>s </p> + <p><b>Animator</b> : This will read the heirarchy of <code>AssimpNodeData</code>, + Interpolate all bones in a recursive manner and then prepare final bone transformation matrices for us that we need. + +</p> + +<p> + Here's the code for <code>Bone</code>... + + <pre><code>struct KeyPosition +{ + glm::vec3 position; + float timeStamp; +}; + +struct KeyRotation +{ + glm::quat orientation; + float timeStamp; +}; + +struct KeyScale +{ + glm::vec3 scale; + float timeStamp; +}; + +class Bone +{ +private: + std::vector&lt;KeyPosition&gt; m_Positions; + std::vector&lt;KeyRotation&gt; m_Rotations; + std::vector&lt;KeyScale&gt; m_Scales; + int m_NumPositions; + int m_NumRotations; + int m_NumScalings; + + glm::mat4 m_LocalTransform; + std::string m_Name; + int m_ID; +public: + /*reads keyframes from aiNodeAnim*/ + Bone(const std::string& name, int ID, const aiNodeAnim* channel) + : + m_Name(name), + m_ID(ID), + m_LocalTransform(1.0f) + { + m_NumPositions = channel->mNumPositionKeys; + + for (int positionIndex = 0; positionIndex &lt; m_NumPositions; ++positionIndex) + { + aiVector3D aiPosition = channel->mPositionKeys[positionIndex].mValue; + float timeStamp = channel->mPositionKeys[positionIndex].mTime; + KeyPosition data; + data.position = AssimpGLMHelpers::GetGLMVec(aiPosition); + data.timeStamp = timeStamp; + m_Positions.push_back(data); + } + + m_NumRotations = channel->mNumRotationKeys; + for (int rotationIndex = 0; rotationIndex &lt; m_NumRotations; ++rotationIndex) + { + aiQuaternion aiOrientation = channel->mRotationKeys[rotationIndex].mValue; + float timeStamp = channel->mRotationKeys[rotationIndex].mTime; + KeyRotation data; + data.orientation = AssimpGLMHelpers::GetGLMQuat(aiOrientation); + data.timeStamp = timeStamp; + m_Rotations.push_back(data); + } + + m_NumScalings = channel->mNumScalingKeys; + for (int keyIndex = 0; keyIndex &lt; m_NumScalings; ++keyIndex) + { + aiVector3D scale = channel->mScalingKeys[keyIndex].mValue; + float timeStamp = channel->mScalingKeys[keyIndex].mTime; + KeyScale data; + data.scale = AssimpGLMHelpers::GetGLMVec(scale); + data.timeStamp = timeStamp; + m_Scales.push_back(data); + } + } + + /* Interpolates b/w positions,rotations & scaling keys based on the curren time of the + animation and prepares the local transformation matrix by combining all keys tranformations */ + void Update(float animationTime) + { + glm::mat4 translation = InterpolatePosition(animationTime); + glm::mat4 rotation = InterpolateRotation(animationTime); + glm::mat4 scale = InterpolateScaling(animationTime); + m_LocalTransform = translation * rotation * scale; + } + + glm::mat4 GetLocalTransform() { return m_LocalTransform; } + std::string GetBoneName() const { return m_Name; } + int GetBoneID() { return m_ID; } + + /* Gets the current index on mKeyPositions to interpolate to based on the current + animation time */ + int GetPositionIndex(float animationTime) + { + for (int index = 0; index &lt; m_NumPositions - 1; ++index) + { + if (animationTime &lt; m_Positions[index + 1].timeStamp) + return index; + } + assert(0); + } + + /* Gets the current index on mKeyRotations to interpolate to based on the current + animation time */ + int GetRotationIndex(float animationTime) + { + for (int index = 0; index &lt; m_NumRotations - 1; ++index) + { + if (animationTime &lt; m_Rotations[index + 1].timeStamp) + return index; + } + assert(0); + } + + /* Gets the current index on mKeyScalings to interpolate to based on the current + animation time */ + int GetScaleIndex(float animationTime) + { + for (int index = 0; index &lt; m_NumScalings - 1; ++index) + { + if (animationTime &lt; m_Scales[index + 1].timeStamp) + return index; + } + assert(0); + } +private: + + /* Gets normalized value for Lerp & Slerp*/ + float GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) + { + float scaleFactor = 0.0f; + float midWayLength = animationTime - lastTimeStamp; + float framesDiff = nextTimeStamp - lastTimeStamp; + scaleFactor = midWayLength / framesDiff; + return scaleFactor; + } + + /* figures out which position keys to interpolate b/w and performs the interpolation + and returns the translation matrix */ + glm::mat4 InterpolatePosition(float animationTime) + { + if (1 == m_NumPositions) + return <function id='55'>glm::translate</function>(glm::mat4(1.0f), m_Positions[0].position); + + int p0Index = GetPositionIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = GetScaleFactor(m_Positions[p0Index].timeStamp, + m_Positions[p1Index].timeStamp, animationTime); + glm::vec3 finalPosition = glm::mix(m_Positions[p0Index].position, + m_Positions[p1Index].position + , scaleFactor); + return <function id='55'>glm::translate</function>(glm::mat4(1.0f), finalPosition); + } + + /* figures out which rotations keys to interpolate b/w and performs the interpolation + and returns the rotation matrix */ + glm::mat4 InterpolateRotation(float animationTime) + { + if (1 == m_NumRotations) + { + auto rotation = glm::normalize(m_Rotations[0].orientation); + return glm::toMat4(rotation); + } + + int p0Index = GetRotationIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = GetScaleFactor(m_Rotations[p0Index].timeStamp, + m_Rotations[p1Index].timeStamp, animationTime); + glm::quat finalRotation = glm::slerp(m_Rotations[p0Index].orientation, + m_Rotations[p1Index].orientation, scaleFactor); + finalRotation = glm::normalize(finalRotation); + return glm::toMat4(finalRotation); + } + + /* figures out which scaling keys to interpolate b/w and performs the interpolation + and returns the scale matrix */ + glm::mat4 Bone::InterpolateScaling(float animationTime) + { + if (1 == m_NumScalings) + return <function id='56'>glm::scale</function>(glm::mat4(1.0f), m_Scales[0].scale); + + int p0Index = GetScaleIndex(animationTime); + int p1Index = p0Index + 1; + float scaleFactor = GetScaleFactor(m_Scales[p0Index].timeStamp, + m_Scales[p1Index].timeStamp, animationTime); + glm::vec3 finalScale = glm::mix(m_Scales[p0Index].scale, + m_Scales[p1Index].scale, scaleFactor); + return <function id='56'>glm::scale</function>(glm::mat4(1.0f), finalScale); + } +}; +</code></pre> + + <p> + + We start by creating 3 structs for our key types. Each struct holds a value and a time stamp. Timestamp tells us at what point of an animation we need to interpolate to its value. + <code>Bone</code> has a constructor which reads from <code>aiNodeAnim</code> and stores keys and their timestamps to <code>mPositionKeys, mRotationKeys & mScalingKeys </code>. The main interpolation process + starts from <code>Update(float animationTime)</code> which gets called every frame. This function calls respective interpolation functions for all key types and combines all final interpolation results + and store it to a 4x4 Matrix <code>m_LocalTransform</code>. The interpolations functions for translation & scale keys are similar but for rotation we are using <code>Slerp</code> to interpolate between quaternions. + Both <code>Lerp</code> & <code>Slerp</code> takes 3 arguments. First argument takes last key, second argument takes next key and third argument takes value of range 0-1,we call it scale factor here. + Let's see how we calculate this scale factor in function <code>GetScaleFactor</code>... + + <p><img src="/img/guest/2020/skeletal_animation/scale_factor.png" alt="skin"/></p> + + <p>In code...</p> + + <p><b> float midWayLength = animationTime - lastTimeStamp; </b></p> + <p><b> float framesDiff = nextTimeStamp - lastTimeStamp;</b></p> + <p><b> scaleFactor = midWayLength / framesDiff; </b></p> + <p></p> + </p> + + Let's move on to <code><b>Animation</b></code> class now... + +<pre><code>struct AssimpNodeData +{ + glm::mat4 transformation; + std::string name; + int childrenCount; + std::vector&lt;AssimpNodeData&gt; children; +}; + +class Animation +{ +public: + Animation() = default; + + Animation(const std::string& animationPath, Model* model) + { + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate); + assert(scene && scene->mRootNode); + auto animation = scene->mAnimations[0]; + m_Duration = animation->mDuration; + m_TicksPerSecond = animation->mTicksPerSecond; + ReadHeirarchyData(m_RootNode, scene->mRootNode); + ReadMissingBones(animation, *model); + } + + ~Animation() + { + } + + Bone* FindBone(const std::string& name) + { + auto iter = std::find_if(m_Bones.begin(), m_Bones.end(), + [&](const Bone& Bone) + { + return Bone.GetBoneName() == name; + } + ); + if (iter == m_Bones.end()) return nullptr; + else return &(*iter); + } + + + inline float GetTicksPerSecond() { return m_TicksPerSecond; } + + inline float GetDuration() { return m_Duration;} + + inline const AssimpNodeData& GetRootNode() { return m_RootNode; } + + inline const std::map&lt;std::string,BoneInfo&gt;& GetBoneIDMap() + { + return m_BoneInfoMap; + } + +private: + void ReadMissingBones(const aiAnimation* animation, Model& model) + { + int size = animation->mNumChannels; + + auto& boneInfoMap = model.GetBoneInfoMap();//getting m_BoneInfoMap from Model class + int& boneCount = model.GetBoneCount(); //getting the m_BoneCounter from Model class + + //reading channels(bones engaged in an animation and their keyframes) + for (int i = 0; i &lt; size; i++) + { + auto channel = animation->mChannels[i]; + std::string boneName = channel->mNodeName.data; + + if (boneInfoMap.find(boneName) == boneInfoMap.end()) + { + boneInfoMap[boneName].id = boneCount; + boneCount++; + } + m_Bones.push_back(Bone(channel->mNodeName.data, + boneInfoMap[channel->mNodeName.data].id, channel)); + } + + m_BoneInfoMap = boneInfoMap; + } + + void ReadHeirarchyData(AssimpNodeData& dest, const aiNode* src) + { + assert(src); + + dest.name = src->mName.data; + dest.transformation = AssimpGLMHelpers::ConvertMatrixToGLMFormat(src->mTransformation); + dest.childrenCount = src->mNumChildren; + + for (int i = 0; i &lt; src->mNumChildren; i++) + { + AssimpNodeData newData; + ReadHeirarchyData(newData, src->mChildren[i]); + dest.children.push_back(newData); + } + } + float m_Duration; + int m_TicksPerSecond; + std::vector&lt;Bone&gt; m_Bones; + AssimpNodeData m_RootNode; + std::map&lt;std::string, BoneInfo&gt; m_BoneInfoMap; +}; +</code></pre> + + <p> Here, creation of an <code>Animation</code> object starts with a constructor. It takes two arguments. First, path to the animation file & second parameter is the <code>Model</code> for this animation. +You will see later ahead why we need this <code>Model</code> reference here. We then create an <code>Assimp::Importer</code> to read the animation file, followed by an <code>assert</code> check which will throw +an error if animation could not be found. Then we read general animation data like how long is this animation which is <code>mDuration</code> and the animation speed represented by <code>mTicksPerSecond</code>. +We then call <code>ReadHeirarchyData</code> which replicates <code>aiNode</code> heirarchy of Assimp and creates heirarchy of <code>AssimpNodeData</code>. +</p> + +<p> Then we call a function called <code>ReadMissingBones</code>. I had to write this function because sometimes when I loaded FBX model separately, it had some bones missing and I found those missing bones in +the animation file. This function reads the missing bones information and stores their information in <code>m_BoneInfoMap</code> of <code>Model</code> and saves a reference of <code>m_BoneInfoMap</code> locally in +the m_BoneInfoMap.</p> + +<p>And we have our animation ready. Now let's look at our final stage, The Animator class...</p> + +<pre><code>class Animator +{ +public: + Animator::Animator(Animation* Animation) + { + m_CurrentTime = 0.0; + m_CurrentAnimation = currentAnimation; + + m_FinalBoneMatrices.reserve(100); + + for (int i = 0; i &lt; 100; i++) + m_FinalBoneMatrices.push_back(glm::mat4(1.0f)); + } + + void Animator::UpdateAnimation(float dt) + { + m_DeltaTime = dt; + if (m_CurrentAnimation) + { + m_CurrentTime += m_CurrentAnimation->GetTicksPerSecond() * dt; + m_CurrentTime = fmod(m_CurrentTime, m_CurrentAnimation->GetDuration()); + CalculateBoneTransform(&m_CurrentAnimation->GetRootNode(), glm::mat4(1.0f)); + } + } + + void Animator::PlayAnimation(Animation* pAnimation) + { + m_CurrentAnimation = pAnimation; + m_CurrentTime = 0.0f; + } + + void Animator::CalculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform) + { + std::string nodeName = node->name; + glm::mat4 nodeTransform = node->transformation; + + Bone* Bone = m_CurrentAnimation->FindBone(nodeName); + + if (Bone) + { + Bone->Update(m_CurrentTime); + nodeTransform = Bone->GetLocalTransform(); + } + + glm::mat4 globalTransformation = parentTransform * nodeTransform; + + auto boneInfoMap = m_CurrentAnimation->GetBoneIDMap(); + if (boneInfoMap.find(nodeName) != boneInfoMap.end()) + { + int index = boneInfoMap[nodeName].id; + glm::mat4 offset = boneInfoMap[nodeName].offset; + m_FinalBoneMatrices[index] = globalTransformation * offset; + } + + for (int i = 0; i &lt; node->childrenCount; i++) + CalculateBoneTransform(&node->children[i], globalTransformation); + } + + std::vector&lt;glm::mat4&gt; GetFinalBoneMatrices() + { + return m_FinalBoneMatrices; + } + +private: + std::vector&lt;glm::mat4&gt; m_FinalBoneMatrices; + Animation* m_CurrentAnimation; + float m_CurrentTime; + float m_DeltaTime; +}; +</code></pre> + +<p> + + <code>Animator</code> constructor takes an animation to play and + then it proceeds to reset the animation time <code>m_CurrentTime</code> to 0. + It also initializes <code>m_FinalBoneMatrices</code> + which is a <code>std::vector&lt;glm::mat4&gt;</code>. + The main point of attention here is <code>UpdateAnimation(float deltaTime)</code> function. + It advances the <code>m_CurrentTime</code> with rate of + <code>m_TicksPerSecond</code> and then calls the <code>CalculateBoneTransform</code> function. + We will pass two arguments in the start, first is the <code>m_RootNode</code> of <code>m_CurrentAnimation</code> + and second is an identity matrix passed as <code>parentTransform</code> This function then check if <code>m_RootNode</code>s bone is engaged in this animation by finding it in <code>m_Bones</code> array of <code>Animation</code>. + If bone is found then it calls <code>Bone.Update()</code> function which interpolates all bones and return local bone transform matrix to + <code>nodeTransform</code>. + But this is local space matrix and will move bone around origin if passed in shaders. So we multiply this <code>nodeTransform</code> with <code>parentTransform</code> and + we store the result in <code>globalTransformation</code>. This would be enough but vertices are still in default model space. + we find offset matrix in <code>m_BoneInfoMap</code> and then multiply it + with <code>globalTransfromMatrix</code>. + We will also get the id index which will be used to write final transformation of this bone to m_FinalBoneMatrices. + </p> + + <p> + Finally! we call <code>CalculateBoneTransform</code> for each child nodes of this node and pass <code>globalTransformation</code> as <code>parentTransform</code>. + We break this recursive loop when there will no children + left to process further. + </p> + +</p> +</p> + +<h3> Let's Animate</h3> + +<p> +Fruit of our hardwork is finally here! Here's how we will play the animation in <code>main.cpp</code> ... +</p> + +<pre><code>int main() +{ + ... + + Model ourModel(FileSystem::getPath("resources/objects/vampire/dancing_vampire.dae")); + Animation danceAnimation(FileSystem::getPath( + "resources/objects/vampire/dancing_vampire.dae"), &ourModel); + Animator animator(&danceAnimation); + + // draw in wireframe + //<function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_LINE); + + // render loop + // ----------- + while (!<function id='14'>glfwWindowShouldClose</function>(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = <function id='47'>glfwGetTime</function>(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + animator.UpdateAnimation(deltaTime); + + // render + // ------ + <function id='13'><function id='10'>glClear</function>Color</function>(0.05f, 0.05f, 0.05f, 1.0f); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // don't forget to enable shader before setting uniforms + ourShader.use(); + + // view/projection transformations + glm::mat4 projection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(camera.Zoom), + (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + ourShader.setMat4("projection", projection); + ourShader.setMat4("view", view); + + auto transforms = animator.GetFinalBoneMatrices(); + for (int i = 0; i &lt; transforms.size(); ++i) + ourShader.setMat4("finalBonesTransformations[" + std::to_string(i) + "]", + transforms[i]); + + // render the loaded model + glm::mat4 model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, glm::vec3(0.0f, -0.4f, 0.0f)); + model = <function id='56'>glm::scale</function>(model, glm::vec3(.5f, .5f, .5f)); + ourShader.setMat4("model", model); + ourModel.Draw(ourShader); + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + <function id='24'>glfwSwapBuffers</function>(window); + <function id='23'>glfwPollEvents</function>(); + } + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + <function id='25'>glfwTerminate</function>(); + return 0; +} +</code></pre> + +<p> + + We start with loading our <code>Model</code> which will setup bone weight data for the shader and then create an <code>Animation</code> by giving it the path. + Then we create our <code>Animator</code> object by passing it the created <code>Animation</code>. In render loop we then update our <code>Animator</code>, take the + final bone transformations and give it to shaders. Here's the output we all have been waiting for... + +</p> + +<img src="/img/guest/2020/skeletal_animation/output.gif" alt="output"> + + <p> Download the model used <a href="/data/models/vampire.zip">here.</a> Note that animations +and meshes are baked in single DAE(collada) file. You can find the full source code <a href="/code_viewer_gh.php?code=src/8.guest/2020/skeletal_animation/skeletal_animation.cpp" target="_blank">here</a>. + +<h3>Further reading</h3> + <ul> + <li><a href="http://www.songho.ca/math/quaternion/quaternion.html" target="_blank"> + Quaternions</a>: An article by songho to understand quaternions in depth.</li> + <li><a href="http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html" target="_blank"> + Skeletal Animation with Assimp</a>: An article by OGL Dev.</li> + <li><a href="https://youtu.be/f3Cr8Yx3GGA" target="_blank"> + Skeletal Animation with Java</a>: A fantastic youtube playlist by Thin Matrix.</li> + <li><a href="https://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php" target="_blank"> + Why Quaternions should be used for Rotation</a>: An awesome gamasutra article.</li> + + </ul> + + + +<author> + <strong>Article by: </strong>Ankit Singh Kushwah<br/> + <strong>Contact: </strong><a href="mailto:eklavyagames@gmail.com" target="_blank">e-mail</a> +</author> + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2021/CSM.html b/pub/Guest-Articles/2021/CSM.html @@ -0,0 +1,787 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">CSM</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2021/CSM</h1> +<p> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping" target="_blank">Shadow mapping</a> as described on LearnOpenGL is a powerful, and relatively simple technique. However, if implemented as-is from the above referred tutorial, the avid OpenGL student will notice a few shortcomings. + </p> + <ul> + <li>The shadow map is always created around the origin, and not on the area the camera is actually looking at. It would be best of course if we could shadow map the whole scene, with sufficient resolution, but on current hardware this is not feasible. In reality we want the shadow maps to be created on objects that are in view, saving our precious GPU memory for things that matter. </li> + <li>The shadow map orthographic projection matrix is not properly fitted onto the view frustum. To achieve the best possible resolution for our shadow maps, the ortho matrix needs to be as tightly fit to the camera frustum as possible, because again: if it’s larger that means that less detail is spent on the objects that are actually visible.</li> + <li>The shadow maps (even with advanced PCF functions) are blurry if we want the shadow rendering distance to be large, as we would in a game with a first-person camera. We can increase the resolution of the shadow maps to mitigate this, but GPU memory is a resource we should be conservative of.</li> + </ul> + <p> + Cascaded shadow mapping is a direct answer to the third point, however while implementing it we will solve the first two points, too. The core insight in cascaded shadow mapping is, that we don’t need the same amount of shadow detail for things that are far from us. We want crisp shadows for stuff that’s near to the near plane, and we are absolutely fine with blurriness for objects that are hundreds of units away: it’s not going to be noticeable at all. How can we achieve this? The answer is beautiful in its simplicity: just render different shadow maps for things that are close and for those that are far away, and sample from them according to the depth of the fragment in the fragment shader. The high-level algorithm is as follows: + </p> + <ul> + <li>Divide our ordinary view frustum into n subfrusta, where the far plane of the <code>i</code>-th frustum is the near plane of the <code>(i+1)</code>-th frustum</li> + <li>Compute the tightly fitting ortho matrix for each frustum</li> + <li>For each frustum render a shadow map as if seen from our directional light</li> + <li>Send all shadow maps to our fragment shader</li> + <li>Render the scene, and according to the fragment’s depth value sample from the correct shadow map</li> + </ul> + <p> + Sounds simple right? Well, it is, but as it often is when it comes to our friend OpenGL: the devil is in the details. + </p> + <img src="/img/guest/2021/CSM/cs_go.png" width="800px"> + <p> + In the above image we can see the edges of shadow cascades in Counter-Strike: Global Offensive. The image was captured on low graphics settings. + </p> + + <h2>World coordinates of the view frustum</h2> + <p> + Before getting our hands dirty with shadows, we need to tackle a more abstract problem: making our projection matrix fit nicely onto a generic frustum. To be able to do this, we need to know the world space coordinates of the frustum in question. While this might sound daunting at first, we already have all the tools necessary in our repertoire. If we think back on the excellent <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank"> coordinate systems</a> tutorial, the beheaded pyramid of the frustum only looks that way in world coordinates, and our view and projection matrices do the job of transforming this unusual shape into the NDC cube. And we know the coordinates of the corners of the NDC cube: the coordinates are in the range <code>[-1,1]</code> on the three axes. Because matrix multiplication is a reversible process, we can apply the inverse of the view and projection matrices on the corner points of the NDC cube to get the frustum corners in world space. + </p> + <math> + $$v_{NDC} = M_{proj} M_{view} v_{world}$$ + $$v_{world} = (M_{proj} M_{view})^{-1} v_{NDC}$$ + </math> + +<pre><code> +std::vector&lt;glm::vec4&gt; getFrustumCornersWorldSpace(const glm::mat4& proj, const glm::mat4& view) +{ + const auto inv = glm::inverse(proj * view); + + std::vector&lt;glm::vec4> frustumCorners; + for (unsigned int x = 0; x &lt; 2; ++x) + { + for (unsigned int y = 0; y &lt; 2; ++y) + { + for (unsigned int z = 0; z &lt; 2; ++z) + { + const glm::vec4 pt = + inv * glm::vec4( + 2.0f * x - 1.0f, + 2.0f * y - 1.0f, + 2.0f * z - 1.0f, + 1.0f); + frustumCorners.push_back(pt / pt.w); + } + } + } + + return frustumCorners; +} +</code></pre> + + <p> + The projection matrix described here is a perspective matrix, using the camera’s aspect ratio and fov, and using the near and far plane of the current frustum being analyzed. The view matrix is the view matrix of our camera. + </p> + +<pre><code> +const auto proj = <function id='58'>glm::perspective</function>( + <function id='63'>glm::radians</function>(camera.Zoom), + (float)SCR_WIDTH / (float)SCR_HEIGHT, + nearPlane, + farPlane +); +</code></pre> + + <img src="/img/guest/2021/CSM/frustum_fitting.png"> + <br> + + <h2>The light view-projection matrix</h2> + <p> + This matrix - as in ordinary shadow mapping – is the product of the view and projection matrix that transforms the scene as if it were seen by the light. Calculating the view matrix is simple, we know the direction our light is coming from, and we know a point in world space that it definitely is looking at: the center of the frustum. The position of the frustum center can be obtained by averaging the coordinates of the frustum corners. (This is so because averaging the coordinates of the near and far plane gives us the center of those rectangles, and taking the midpoint of these two points gives us the center of the frustum.) + </p> + <pre><code> +glm::vec3 center = glm::vec3(0, 0, 0); +for (const auto& v : corners) +{ + center += glm::vec3(v); +} +center /= corners.size(); + +const auto lightView = <function id='62'>glm::lookAt</function>( + center + lightDir, + center, + glm::vec3(0.0f, 1.0f, 0.0f) +); + </code></pre> + <p> + The projection matrix is bit more complex. Because the light in question is a directional light, the matrix needs to be an orthographic projection matrix, this much is clear. To understand how we determine the left, right, top etc. parameters of the matrix imagine the scene you are drawing from the perspective of the light. From this viewpoint the shadow map we are trying to render is going to be an axis aligned rectangle, and this rectangle – as we established before – needs to fit on the frustum tightly. So we need to obtain the coordinates of the frustum in this space, and take the maximum and minimum of the coordinates along the coordinate axes. While this might sound daunting at first, this perspective is exactly what our light view matrix transformation gives us. We need to transform the frustum corner points in the light view space, and find the maximum and minimum coordinates. + </p> + + <pre><code> +float minX = std::numeric_limits&lt;float>::max(); +float maxX = std::numeric_limits&lt;float>::min(); +float minY = std::numeric_limits&lt;float>::max(); +float maxY = std::numeric_limits&lt;float>::min(); +float minZ = std::numeric_limits&lt;float>::max(); +float maxZ = std::numeric_limits&lt;float>::min(); +for (const auto& v : corners) +{ + const auto trf = lightView * v; + minX = std::min(minX, trf.x); + maxX = std::max(maxX, trf.x); + minY = std::min(minY, trf.y); + maxY = std::max(maxY, trf.y); + minZ = std::min(minZ, trf.z); + maxZ = std::max(maxZ, trf.z); +} + </code></pre> + + <p> + We are going to create our projection matrix from the product of two matrices. First, we are going to create an ortho projection matrix, with the left, right, top, bottom parameters set to <code>-1</code> or <code>1</code>, and the z values set to <var>minZ</var> and <var>maxZ</var>. This creates a 3D rectangle sitting on the origin with width and height of <code>2</code>, and depth of (<var>maxZ</var> – <var>minZ</var>). In the code we increase the amount of space covered by <var>minZ</var> and <var>maxZ</var> by multiplying or dividing them with a <var>zMult</var>. This is because we want to include geometry which is behind or in front of our frustum in camera space. Think about it: not only geometry which is in the frustum can cast shadows on a surface in the frustum! + </p> + +<pre><code> +// Tune this parameter according to the scene +constexpr float zMult = 10.0f; +if (minZ &lt; 0) +{ + minZ *= zMult; +} +else +{ + minZ /= zMult; +} +if (maxZ &lt; 0) +{ + maxZ /= zMult; +} +else +{ + maxZ *= zMult; +} + +const glm::mat4 lpMatrix = <function id='59'>glm::ortho</function>(-1.0f, 1.0f, -1.0f, 1.0f, minZ, maxZ); +</code></pre> + <p> + The other part of our projection matrix is going to be a crop matrix, which moves the rectangle onto the frustum in light view space. Starting from a unit matrix, we need to set the x and y scaling components, and the x and y offset components of the matrix. + </p> + + <math> + $$S_x = {2 \over M_x - m_x}$$ + $$S_y = {2 \over M_y - m_y}$$ + $$O_x = -0.5(M_x + m_x)S_x$$ + $$O_y = -0.5(M_y + m_y)S_y$$ + $$C = \begin{bmatrix}S_x & 0 & 0 & O_x \\0 & S_y & 0 & O_y \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1\end{bmatrix}$$ + </math> + + <p> + Let’s look at S<sub>x</sub>. It needs to shrink down the frustum bounding rectangle to a unit size, this is achieved by dividing by the width of the rectangle (M<sub>x</sub> – m<sub>x</sub>). However, we need to scale by <code>2</code>, because our 3D rectangle sitting on the origin has a width of <code>2</code>. S<sub>y</sub> is analogous to this. + </p> + <img src="/img/guest/2021/CSM/frustum_cropping.png" width="800px"> + <p> + For the x offset, we need to multiply by the negative halfway point between M<sub>x</sub> and m<sub>x</sub>, and scale by S<sub>x</sub>. And the y offset is analogous. + </p> + +<pre><code> +const float scaleX = 2.0f / (maxX - minX); +const float scaleY = 2.0f / (maxY - minY); +const float offsetX = -0.5f * (minX + maxX) * scaleX; +const float offsetY = -0.5f * (minY + maxY) * scaleY; + +glm::mat4 cropMatrix(1.0f); +cropMatrix[0][0] = scaleX; +cropMatrix[1][1] = scaleY; +cropMatrix[3][0] = offsetX; +cropMatrix[3][1] = offsetY; + +return cropMatrix * lpMatrix * lightView; +</code></pre> + + <p> + Multiplying the view, projection and crop matrices together, we get the view-projection matrix of the light for the given frustum. We need to do this procedure for every frustum in our cascade. + </p> + + <h2>2D array textures</h2> + <p> + While we let our stomachs digest what we learned about frustum fitting we should figure out how to store our shadow maps. In principle there is no limit on how many cascades we can do, so hardcoding an arbitrary value doesn’t seem like a wise idea. Also, it quickly becomes tiresome typing out and binding sampler2Ds for our shaders. OpenGL has a good solution to this problem in the form of <def>2D array textures</def>. This object is an array of textures, which have the same dimensions. To use them in shaders declare them like this: + </p> + +<pre><code> +uniform sampler2DArray shadowMap; +</code></pre> + + <p> + To sample from them we can use the regular texture function with a vec3 parameter for texture coordinates, the third dimension specifying which layer to sample from, starting from <code>0</code>. + </p> + +<pre><code> +texture(depthMap, vec3(TexCoords, currentLayer)) +</code></pre> + + <p> + Creating our array texture is slightly different than creating a regular old texture2D. Instead of <function id='52'>glTexImage2D</function> we use glTexImage3D to allocate memory, and when binding the texture to the framebuffer we use glFramebufferTexture instead of <function id='81'>glFramebufferTexture2D</function>. The parameters of these functions are somewhat self-explanatory if we know the old versions. When calling the OpenGL functions, we need to pass <var>GL_TEXTURE_2D_ARRAY</var> instead of <var>GL_TEXTURE_2D</var> as the target, to tell OpenGL what kind of texture we are dealing with. + </p> + +<pre><code> +<function id='76'>glGenFramebuffers</function>(1, &lightFBO); + +<function id='50'>glGenTextures</function>(1, &lightDepthMaps); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D_ARRAY, lightDepthMaps); +glTexImage3D( + GL_TEXTURE_2D_ARRAY, + 0, + GL_DEPTH_COMPONENT32F, + depthMapResolution, + depthMapResolution, + int(shadowCascadeLevels.size()) + 1, + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + nullptr); + +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + +constexpr float bordercolor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; +<function id='15'>glTexParameter</function>fv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, bordercolor); + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, lightFBO); +glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, lightDepthMaps, 0); +glDrawBuffer(GL_NONE); +glReadBuffer(GL_NONE); + +int status = <function id='79'>glCheckFramebufferStatus</function>(GL_FRAMEBUFFER); +if (status != GL_FRAMEBUFFER_COMPLETE) +{ + std::cout &lt;&lt; "ERROR::FRAMEBUFFER:: Framebuffer is not complete!"; + throw 0; +} + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + + <p> + Take care when binding this texture to a sampler. Again: we need to use <var>GL_TEXTURE_2D_ARRAY</var> as the target parameter. + </p> + +<pre><code> +<function id='49'>glActiveTexture</function>(GL_TEXTURE1); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D_ARRAY, lightDepthMaps); +</code></pre> + + <p> + So far so good, now we know the semantics of using a texture array. It all seems straightforward, but OpenGL has one more curveball to throw at us: we can’t render into this texture the ordinary way, we need to do something called <def>layered rendering</def>. This process is very similar to what we did with <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows" target="_blank">cubemaps and pointlights</a> , we coax the <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader" target="_blank">geometry shader</a> into generating multiple layers of geometry for us at the same time. If we recall our depthmap shader is very simple: transform the vertices to light space in the vertex stage, and let the hardware do the rest with an empty fragment shader. In our new depthmap shader we are going to only do the world space transformation in the vertex shader. + </p> + +<pre><code> +#version 460 core +layout (location = 0) in vec3 aPos; + +uniform mat4 model; + +void main() +{ + gl_Position = model * vec4(aPos, 1.0); +} +</code></pre> + + <p> + The newly inserted geometry shader will look something like this: + </p> + +<pre><code> +#version 460 core + +layout(triangles, invocations = 5) in; +layout(triangle_strip, max_vertices = 3) out; + +layout (std140, binding = 0) uniform LightSpaceMatrices +{ + mat4 lightSpaceMatrices[16]; +}; + +void main() +{ + for (int i = 0; i &lt; 3; ++i) + { + gl_Position = + lightSpaceMatrices[gl_InvocationID] * gl_in[i].gl_Position; + gl_Layer = gl_InvocationID; + EmitVertex(); + } + EndPrimitive(); +} +</code></pre> + + <p> + The input declaration has a new member, specifying the <def>invocation count</def>. This number means, that this shader will be instanced, these instances running in parallel, and we can refer the current instance by the inbuilt variable <var>gl_InvocationID</var>. We will use this number in the shader code to reference which layer of the array texture we are rendering to, and which shadow matrix we are going to use to do the light space transform. We are iterating over all triangles, and setting <var>gl_Layer</var> and <var>gl_Position</var> accordingly. + </p> + + <note> + I strongly suggest modifying your Shader class in your engine to enable the possibility of inserting variables into the shader code before shader compilation, so that you can make the <i>invocations</i> parameter dynamic. This way if you modify the number of cascades in the C++ code you dont have to modify the shader itself, removing one cog from the complex machine that is your engine. I didn't include this in the tutorial for the sake of simplicity. + </note> + + <p> + The fragment shader remains the same empty, passthrough shader. + </p> + +<pre><code> +#version 460 core + +void main() +{ +} +</code></pre> + + <p> + Our draw call invoking the shader looks something like this: + </p> + +<pre><code> +simpleDepthShader.use(); + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, lightFBO); +glFramebufferTexture(GL_FRAMEBUFFER, GL_TEXTURE_2D_ARRAY, lightDepthMaps, 0); +<function id='22'>glViewport</function>(0, 0, depthMapResolution, depthMapResolution); +<function id='10'>glClear</function>(GL_DEPTH_BUFFER_BIT); +<function id='74'>glCullFace</function>(GL_FRONT); // peter panning +renderScene(simpleDepthShader); +<function id='74'>glCullFace</function>(GL_BACK); +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + + <img src="/img/guest/2021/CSM/cascades.png" width="800px"> + + <h2>Scene rendering</h2> + <p> + Now the only thing remaining is doing the actual shadow rendering. In our ordinary phong/deferred fragment shader where we calculate whether the current fragment is occluded or not, we need to insert some logic to decide which light space matrix to use, and which texture to sample from. + </p> + +<pre><code> +// select cascade layer +vec4 fragPosViewSpace = view * vec4(fragPosWorldSpace, 1.0); +float depthValue = abs(fragPosViewSpace.z); + +int layer = -1; +for (int i = 0; i &lt; cascadeCount; ++i) +{ + if (depthValue &lt; cascadePlaneDistances[i]) + { + layer = i; + break; + } +} +if (layer == -1) +{ + layer = cascadeCount; +} + +vec4 fragPosLightSpace = lightSpaceMatrices[layer] * vec4(fragPosWorldSpace, 1.0); +</code></pre> + + <p> + If you remember to prevent shadow acne we applied a depth bias to our image. We need to do the same here, but keep in mind that we are dealing with multiple shadow maps, and on each of them the pixels cover a widely different amount of space, and a unit increase in pixel value means different depth increase in all of them. Because of this we need to apply a different bias depending on which shadow map we sample from. In my experience scaling the bias inversely proportionally with the far plane works nicely. + </p> + +<pre><code> +// perform perspective divide +vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; +// transform to [0,1] range +projCoords = projCoords * 0.5 + 0.5; + +// get depth of current fragment from light's perspective +float currentDepth = projCoords.z; +if (currentDepth &gt; 1.0) +{ + return 0.0; +} +// calculate bias (based on depth map resolution and slope) +vec3 normal = normalize(fs_in.Normal); +float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); +if (layer == cascadeCount) +{ + bias *= 1 / (farPlane * 0.5f); +} +else +{ + bias *= 1 / (cascadePlaneDistances[layer] * 0.5f); +} +</code></pre> + + <note> + Please note that there are different strategies for applying bias when dealing with shadow maps. I will link to a few sources detailing these in the citations section. + </note> + + <p> + The rest of the function is the same as before, the only difference is that we are sampling from a 2D array texture, hence we need to add a third parameter to the <fun>texture</fun> and the <fun>textureSize</fun> functions. + </p> + +<pre><code> +// PCF +float shadow = 0.0; +vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); +for(int x = -1; x &lt;= 1; ++x) +{ + for(int y = -1; y &lt;= 1; ++y) + { + float pcfDepth = texture( + shadowMap, + vec3(projCoords.xy + vec2(x, y) * texelSize, + layer) + ).r; + shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0; + } +} +shadow /= 9.0; + +// keep the shadow at 0.0 when outside the far_plane region of the light's frustum. +if(projCoords.z &gt; 1.0) +{ + shadow = 0.0; +} + +return shadow; +</code></pre> + + <p> + And that's it! If we did everything correctly we should see that the renderer switches between shadow maps based on the distance. Try setting some unreasonable cascade plane distances (for example only one, which is a few units from the camera) to see if the code really does work. You should see a noticable degradation in shadow quality between the two sides of the plane. If you see moire artifacts on the screen try changing around bias parameters a bit. + </p> + + <img src="/img/guest/2021/CSM/demoscene.png" width="800px"> + + <p> + You can find the full source code for the cascaded shadow mapping demo <a href="/code_viewer_gh.php?code=src/8.guest/2021/2.csm/shadow_mapping.cpp" target="_blank">here</a>. + </p> + + <h2>Closing thoughts</h2> + <p> + In the sample project provided you can toggle depthmap visualization by pressing <kbd>F</kbd>. When in depthmap visualization mode you can press the <kbd>+</kbd> key to swap between the different layers. + </p> + <p> + When browsing through the code you might wonder why is the UBO array length <code>16</code>. This is just an arbitrary choice, to me it seemed unlikely that anyone would use more than <code>16</code> shadow cascades, so this seemed like a nice number to allocate. + </p> + + <h2>Additional Resources</h2> + <ul> + <li><a href="https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded_shadow_maps.pdf" target="_blank">NVIDIA paper on the subject:</a> incomprehensible in my opinion but has to be mentioned</li> + <li><a href="https://www.gamedev.net/forums/topic/672664-fitting-directional-light-in-view-frustum/?page=1" target="_blank">A series of incredibly helpful and useful forum posts</a></li> + <li><a href="https://ogldev.org/www/tutorial49/tutorial49.html" target="_blank">Another interesting tutorial from OGLDev</a></li> + <li><a href="https://docs.microsoft.com/en-us/windows/win32/dxtecharts/cascaded-shadow-maps" target="_blank">An article from Microsoft:</a> nice pictures illustrating some issues with CSM</li> + <li><a href="https://digitalrune.github.io/DigitalRune-Documentation/html/3f4d959e-9c98-4a97-8d85-7a73c26145d7.htm" target="_blank">An article about shadow bias</a></li> + <li><a href="http://c0de517e.blogspot.com/2011/05/shadowmap-bias-notes.html" target="_blank">Some informative drawings about shadow bias strategies</a></li> + </ul> + <br> + +<author> + <strong>Article by: </strong>Márton Árbócz<br/> + <!--<strong>Contact: </strong><a href="mailto:eklavyagames@gmail.com" target="_blank">e-mail</a>--> +</author> + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2021/Scene/Frustum-Culling.html b/pub/Guest-Articles/2021/Scene/Frustum-Culling.html @@ -0,0 +1,838 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Frustum Culling</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2021/Scene/Frustum-Culling</h1> +<p> + Now we know how to create a Scene graph and organize your object in a scene, we are going to see how to limit your GPU usage thanks to a technical name's the frustum culling. + This technique is simple to understand. + Instead of sending all information to your GPU, you will sort visible and invisible elements and render only visible elements. + Thanks to this technique, you will earn GPU compute time. + You need to know that when information travels toward another unit in your computer, it takes a long time. + For example, information from your GPU to your ram takes time. + It's the same if you want to send information from your CPU to your GPU like a model matrice. + It's for this reason that the "draw instance" is so powerful. + You send a large block to your GPU instead of sending elements one by one. + But this technique isn’t free. + To sort your element, you need to create a physical scene to compute some stuff with math. + This chapter will start with an introduction to the mathematical concept that will allow us to understand how frustum culling works. + Next, we are going to implement it. + Finally, we are going to study possible optimizations and talk about the balance of the technical. + </p> + + <video width="850" controls> + <source src="/img\guest\2021\Frustum_culling\frustumExample.mp4" type="video/mp4"> + Your browser does not support the video tag. + </video> + + <p> + In this video illustrating frustum culling in a forest, the yellow and red shape on the left side is the bounding volume that contains the mesh. + Red color means that the mesh is not visible and not sent to the GPU. + Yellow means that the mesh is rendered. + As you can see lots of things are rendered and few are visible for the player. + </p> + + <h2>Mathematical concept</h2> + + <p> + Let's start the mathematical parts from top to bottom. + Firstly, what is a frustum? + As we can see in <a href="https://en.wikipedia.org/wiki/Frustum" target="_blank"> Wikipedia</a>, frustum is a portion of a solid like a cone or pyramid. + The frustum is usually used in game engine to speak about the camera frustum. + Camera frustum represents the zone of vision of a camera. + Without limit, we have a pyramid but with near and far we have a frustum. + </p> + + <img src="/img/guest/2021/Frustum_culling/VisualCameraFrustum.png" alt="Camera frustum shape"/> + + <p> + How to mathematically represent a frustum? + Thanks to 6 plans: near, far, right, left top and bottom plans. + So, an object is visible if it is forward or on the 6 plans. + Mathematically a plan is represented with a normal vector and distance to the origin. + A plan doesn't have any size or limit as a quad. + </p> + + <img src="/img/guest/2021/Frustum_culling/plan.png" width="600" alt="Plan representation"/> + + <p> + So, create a struct to represent a plan: + </p> + <pre><code> +struct Plan +{ + // unit vector + glm::vec3 normal = { 0.f, 1.f, 0.f }; + + // distance from origin to the nearest point in the plan + float distance = 0.f; + + [...] +}; + </code></pre> + + <p> + We can now create <fun>Frustum</fun> structure: + </p> + + <pre><code> +struct Frustum +{ + Plan topFace; + Plan bottomFace; + + Plan rightFace; + Plan leftFace; + + Plan farFace; + Plan nearFace; +}; + </code></pre> + + <p> + Reminder: a plan can be built with a point and a normal. + For the near, the normal is the front vector of the camera. + For the far plan, it's the opposite. + The normal of the right face we will need to do a cross product. + The cross product is the second wonderful tool for the programmer who likes vectors. + It allows you to get a perpendicular vector to a plan created with two vectors. + To go forward, we need to do the cross product of the right axis per up. + We will use it like that: + </p> + + <img src="/img/guest/2021/Frustum_culling/RightNormal.png" alt="Plan representation"/> + + <p> + But to know the direction of each vector from the camera to the far plan we will know the side length of the far quad: + </p> + <img src="/img/guest/2021/Frustum_culling/hAndVSide.png" alt="Plan representation"/> + + <p> + hSide and vSide are the far quad limited by the other plans of the camera frustum. + To compute its edge, we will need of trigonometry. + As you can see in the image above, we have two rectangle triangles and we can apply the trigonometric functions. + So, we would like to obtain vSide which is the opposite side and we have zFar that is the adjacent side of the camera. + Tan of fovY is equal to the opposite side (vSide) divided by the adjacent side (zFar). + In conclusion, if I move the adjacent side on the left on our equation, tan of fovY multiplied by the zFar is equal to the vSide. + We now need to compute hSide. + Thanks to the aspect that is a ratio of the width by the height, we can easily obtain it. + So, hSide is equal to the vSide multiplied by the aspect as you can see on the right side of the image above. + We can now implement our function: + </p> + + <pre><code> +Frustum createFrustumFromCamera(const Camera& cam, float aspect, float fovY, + float zNear, float zFar) +{ + Frustum frustum; + const float halfVSide = zFar * tanf(fovY * .5f); + const float halfHSide = halfVSide * aspect; + const glm::vec3 frontMultFar = zFar * cam.Front; + + frustum.nearFace = { cam.Position + zNear * cam.Front, cam.Front }; + frustum.farFace = { cam.Position + frontMultFar, -cam.Front }; + frustum.rightFace = { cam.Position, + <function id='61'>glm::cross</function>(cam.Up,frontMultFar + cam.Right * halfHSide) }; + frustum.leftFace = { cam.Position, + <function id='61'>glm::cross</function>(frontMultFar - cam.Right * halfHSide, cam.Up) }; + frustum.topFace = { cam.Position, + <function id='61'>glm::cross</function>(cam.Right, frontMultFar - cam.Up * halfVSide) }; + frustum.bottomFace = { cam.Position, + <function id='61'>glm::cross</function>(frontMultFar + cam.Up * halfVSide, cam.Right) }; + + return frustum; +} +</code></pre> +<note> + In this example, the camera doesn't know the near, aspect but I encourage you to include this variable inside your Camera class. +</note> + + <h3>Bounding volume</h3> + + <p> + Let's take a minute to imagine an algorithm that can detect collisions with your mesh (with all types of polygons in general) and a plan. + You will start to say that image is an algorithm that checks if a triangle is on or outside the plane. + This algorithm looks pretty and fast! But now imagine that you have hundreds of mesh with thousands of triangles each one. + Your algorithm will sign the death of your frame rate fastly. + Another method is to wrap your objects in another geometrical object with simplest properties such as a sphere, a box, a capsule... + Now our algorithm looks possible without creating a framerate black hole. + Its shape is called bounding volume and allows us to create a simpler shape than our mesh to simplify the process. + All shapes have their own properties and can correspond plus or minus to our mesh. + </p> + <img src="/img/guest/2021/Frustum_culling/boundingVolumeQuality.png" alt="Bounding volume quality vs computation speed"/> + + <p> + All shapes also have their own compute complexity. + The <a href="https://en.wikipedia.org/wiki/Bounding_volume" target="_blank">article</a> on Wikipedia is very nice and describes some bounding volumes with their balance and application. + In this article, we are going to see 2 bounding volumes: the sphere and the AABB. + Let's create a simple abstract struct Volume that represent all our bounding volumes: + </p> + + <pre><code> +struct Volume +{ + virtual bool isOnFrustum(const Frustum& camFrustum, + const Transform& modelTransform) const = 0; +}; + </code></pre> + + <h4>Sphere</h4> + + <img src="/img/guest/2021/Frustum_culling/boundingSphere.png" alt="Bounding sphere example"/> + + <p> + The bounding sphere is the simplest shape to represent a bounding volume. + It is represented by center and radius. + A sphere is ideal to encapsulate mesh with any rotation. + It must be adjusted with the scale and position of the object. + We can create struct Sphere that inheritance from volume struct: + </p> + + <pre><code> +struct Sphere : public Volume +{ + glm::vec3 center{ 0.f, 0.f, 0.f }; + float radius{ 0.f }; + + [...] +} + </code></pre> + + <p> + This struct doesn't compile because we haven't defined the function isOnFrustum. + Let's make it. + Remember that our bounding volume is processed thanks to our meshes. + That assumes that we will need to apply a transform to our bounding volume to apply it. + As we have seen in the previous chapter, we will apply the transformation to a scene graph. + </p> + + <pre><code> +bool isOnFrustum(const Frustum& camFrustum, const Transform& transform) const final +{ + //Get global scale is computed by doing the magnitude of + //X, Y and Z model matrix's column. + const glm::vec3 globalScale = transform.getGlobalScale(); + + //Get our global center with process it with the global model matrix of our transform + const glm::vec3 globalCenter{ transform.getModelMatrix() * glm::vec4(center, 1.f) }; + + //To wrap correctly our shape, we need the maximum scale scalar. + const float maxScale = std::max(std::max(globalScale.x, globalScale.y), globalScale.z); + + //Max scale is assuming for the diameter. So, we need the half to apply it to our radius + Sphere globalSphere(globalCenter, radius * (maxScale * 0.5f)); + + //Check Firstly the result that have the most chance + //to faillure to avoid to call all functions. + return (globalSphere.isOnOrForwardPlan(camFrustum.leftFace) && + globalSphere.isOnOrForwardPlan(camFrustum.rightFace) && + globalSphere.isOnOrForwardPlan(camFrustum.farFace) && + globalSphere.isOnOrForwardPlan(camFrustum.nearFace) && + globalSphere.isOnOrForwardPlan(camFrustum.topFace) && + globalSphere.isOnOrForwardPlan(camFrustum.bottomFace)); +}; + </code></pre> + <note> + To compute the globalCenter we can’t only add the current center with the global position because we need to apply translation caused by rotation and scale. + This is the reason why we use the model matrix. + </note> + + <p> + As you can see, we used a function undefined for now called <fun>isOnOrForwardPlan</fun>. + This implementation method is called top/down programming and consists to create a high-level function to determine which kind of function need to be implemented. + It avoids to implement too many unused functions that can be the case in "bottom/up". + So to understand how this function works, let's make a drawing : + </p> + + <img src="/img/guest/2021/Frustum_culling/SpherePlanDetection.png" width="400" height="400" alt="Sphere plan collision shema"/> + + <p> + We can see 3 possible cases: Sphere is inside the plan, back or forward. + To detect when a sphere is colliding with a plan we need to compute the nearest distance from the center of the sphere to the plan. + When we have this distance, we need to compare this distance with radius. + </p> + + <pre><code> +bool isOnOrForwardPlan(const Plan& plan) const +{ + return plan.getSignedDistanceToPlan(center) > -radius; +} + </code></pre> + + <note> + We can see the problem in the other way and create a function called <fun>isOnBackwardPlan</fun>. + To use it we simply need to check if bounding volume IS NOT on the backward plan + </note> + + <p> + Now we need to create the function <fun>getSignedDistanceToPlan</fun> in the </un>Plan</fun> structure. + Let me realize my most beautiful paint for you : + </p> + +<img src="/img/guest/2021/Frustum_culling/SignedDistanceDraw.png" width="400" height="400" alt="Signed distance to plan shema"/> + + <p> + Signed distance is a positive distance from a point if this point is forward the plan. + Otherwise this distance will be negative. + To obtain it, we will need to call a friend: The dot product. + Dot product allows us to obtain the projection from a vector to another. + The result of the dot product is a scale and this scalar is a distance. + If both vectors go oppositely, the dot product will be negative. + Thanks to it, we will obtain the horizontal scale component of a vector in the same direction as the normal of the plan. + Next, we will need to subtract this dot product by the nearest distance from the plan to the origin. + Hereafter you will find the implementation of this function : + </p> + + <pre><code> +float getSignedDistanceToPlan(const glm::vec3& point) const +{ + return glm::dot(normal, point) - distance; +} + </code></pre> + + <h4>AABB</h4> +<img src="/img/guest/2021/Frustum_culling/boundingAABB.png" alt="Bounding AABB example"/> + + <p> + AABB is the acronym of Axis aligned bounding box. + It means that this volume has the same orientation as the world. + It can be constructed as different can be we generally create it with its center and its half extension. + The half extension is a distance from center to the edge in the direction of an axis. + The half extension can be called Ii, Ij, Ik. In this chapter, we will call it Ix, Iy, Iz. + </p> + +<img src="/img/guest/2021/Frustum_culling/AABBRepresentation.png" width="400" alt="AABB representation"/> + + <p> + Let's make the base of this structure with few constructors to made its creation the simplest + </p> + + <pre><code> +struct AABB : public BoundingVolume +{ + glm::vec3 center{ 0.f, 0.f, 0.f }; + glm::vec3 extents{ 0.f, 0.f, 0.f }; + + AABB(const glm::vec3& min, const glm::vec3& max) + : BoundingVolume{}, + center{ (max + min) * 0.5f }, + extents{ max.x - center.x, max.y - center.y, max.z - center.z } + {} + + AABB(const glm::vec3& inCenter, float iI, float iJ, float iK) + : BoundingVolume{}, center{ inCenter }, extents{ iI, iJ, iK } + {} + + [...] +}; + </code></pre> + + <p> + We now need to add the function <fun>isOnFrustum</fun> and <fun>isOnOrForwardPlan</fun>. + The problem is not easy as a bounding sphere because if I rotate my mesh, the AABB will need to be adjusted. + An image talks much than a text : + </p> + +<img src="/img/guest/2021/Frustum_culling/AABBProblem.png" alt="AABB rotation probleme"/> + + <p> + To solve this problem lets draw it : + </p> + +<img src="/img/guest/2021/Frustum_culling/AABB orientation.png" alt="AABB orientation problem explication"/> + + <p> + Crazy guys want to rotate our beautiful Eiffel tower but we can see that after its rotation, the AABB is not the same. + To make the Shema more readable, assume that referential is not a unit and represented the half extension with the orientation of the mesh. + To adjust it, we can see in the third picture that the new extension is the sum of the dot product with the world axis and the scaled referential of our mesh. + The problem is seen in 2D but in 3D it's the same thing. Let's implement the function to do it. + </p> + + <pre><code> +bool isOnFrustum(const Frustum& camFrustum, const Transform& transform) const final +{ + //Get global scale thanks to our transform + const glm::vec3 globalCenter{ transform.getModelMatrix() * glm::vec4(center, 1.f) }; + + // Scaled orientation + const glm::vec3 right = transform.getRight() * extents.x; + const glm::vec3 up = transform.getUp() * extents.y; + const glm::vec3 forward = transform.getForward() * extents.z; + + const float newIi = std::abs(glm::dot(glm::vec3{ 1.f, 0.f, 0.f }, right)) + + std::abs(glm::dot(glm::vec3{ 1.f, 0.f, 0.f }, up)) + + std::abs(glm::dot(glm::vec3{ 1.f, 0.f, 0.f }, forward)); + + const float newIj = std::abs(glm::dot(glm::vec3{ 0.f, 1.f, 0.f }, right)) + + std::abs(glm::dot(glm::vec3{ 0.f, 1.f, 0.f }, up)) + + std::abs(glm::dot(glm::vec3{ 0.f, 1.f, 0.f }, forward)); + + const float newIk = std::abs(glm::dot(glm::vec3{ 0.f, 0.f, 1.f }, right)) + + std::abs(glm::dot(glm::vec3{ 0.f, 0.f, 1.f }, up)) + + std::abs(glm::dot(glm::vec3{ 0.f, 0.f, 1.f }, forward)); + + //We not need to divise scale because it's based on the half extention of the AABB + const AABB globalAABB(globalCenter, newIi, newIj, newIk); + + return (globalAABB.isOnOrForwardPlan(camFrustum.leftFace) && + globalAABB.isOnOrForwardPlan(camFrustum.rightFace) && + globalAABB.isOnOrForwardPlan(camFrustum.topFace) && + globalAABB.isOnOrForwardPlan(camFrustum.bottomFace) && + globalAABB.isOnOrForwardPlan(camFrustum.nearFace) && + globalAABB.isOnOrForwardPlan(camFrustum.farFace)); +}; + </code></pre> + + <p> + For the function <fun>isOnOrForwardPlan</fun>, I have taken an algorithm that I found in a wonderful <a href="https://gdbooks.gitbooks.io/3dcollisions/content/Chapter2/static_aabb_plan.html" target="_blank">article</a>. + I invite you to have a look at it if you want to understand how it works. + I just modify the result of its algorithm to check if the AABB is on or forward my plan. + </p> + + <pre><code> +bool isOnOrForwardPlan(const Plan& plan) const +{ + // Compute the projection interval radius of b onto L(t) = b.c + t * p.n + const float r = extents.x * std::abs(plan.normal.x) + + extents.y * std::abs(plan.normal.y) + extents.z * std::abs(plan.normal.z); + + return -r <= plan.getSignedDistanceToPlan(center); +} + </code></pre> + + <p> + To check if our algorithm works, we need to check that every object disappeared in front of our camera when we moved. + Then, we can add a counter that is incremented if an object is displayed and another for the total displayed in our console. + </p> + + <pre><code> +// in main.cpp main lopp +unsigned int total = 0, display = 0; +ourEntity.drawSelfAndChild(camFrustum, ourShader, display, total); +std::cout << "Total process in CPU : " << total; +std::cout << " / Total send to GPU : " << display << std::endl; + +// In the drawSelfAndChild function of entity +void drawSelfAndChild(const Frustum& frustum, Shader& ourShader, + unsigned int& display, unsigned int& total) +{ + if (boundingVolume->isOnFrustum(frustum, transform)) + { + ourShader.setMat4("model", transform.getModelMatrix()); + pModel->Draw(ourShader); + display++; + } + total++; + + for (auto&& child : children) + { + child->drawSelfAndChild(frustum, ourShader, display, total); + } +} + </code></pre> + +<img src="/img/guest/2021/Frustum_culling/result.png" alt="Result"/> + + <p> + Ta-dah ! The average of objects sent to our GPUrepresents now about 15% of the total and is only divided by 6. + A wonderful result if your GPU process is the bottleneck because of your shader or number of polygons. + You can find the code <a href="https://learnopengl.com/code_viewer_gh.php?code=src/8.guest/2021/1.scene/2.frustum_culling/frustum_culling.cpp" target="_blank">here</a>. + </p> + + <h2>Optimization</h2> + <p> + Now you know how to make your frustum culling. + Frustum culling can be useful to avoid computation of things that are not visible. + You can use it to not compute the animation state of your entity, simplify its AI... + For this reason, I advise you to add a IsInFrustum flag in your entity and do a frustum culling pass that fills this variable. + </p> + <h3>Space partitionning</h3> + <p> + In our example, frustum culling is a good balance with a small number of entities in the CPU. + If you want to optimize your detection, you now will need to partition your space. + To do it, a lot of algorithms exist and each has interesting properties which depend on your usage : + - BSH (Bounding sphere hierarchy or tree) : + Different kinds exist. The simplest implementation is to wrap both nearest objects in a sphere. + Wrap this sphere with another group or objetc etc... + <img src="/img/guest/2021/Frustum_culling/BSH.png" width="400" height="400" alt="BSH example"/> + </p> + <note> + In this example, only 2 checks allow us to know that 3 objects are in frustum instead of 6 because if the bounding sphere is totally inside the frustum all its content is also inside. + If the bounding sphere is not inside when needed to inter and check its content. + </note> + <p> + - <a href="https://en.wikipedia.org/wiki/Quadtree" target="_blank">Quadtree</a> : + The main idea is that you will split space into 4 zones that can be split into four zones etc... until an object wasn't wrapped alone. + Your object will be the leaf of this diagram. + The quadtree is very nice to partition 2D spaces but also if you don't need to partition height. It can be very useful in strategy games like 4x (like age of empire, war selection...) because you don't need height partitioning. + <img src="/img/guest/2021/Frustum_culling/quadtree.png" width="400" height="400" alt="Quatree example"/> + - <a href="https://en.wikipedia.org/wiki/Octree" target="_blank">Octree</a> : + It's like a quadtree but with 8 nodes. It's nice if you have a 3D game with elements in different height levels. + <img src="/img/guest/2021/Frustum_culling/octree.png" width="500" alt="Octree example"/> + - <a href="https://en.wikipedia.org/wiki/Binary_space_partitioning" target="_blank">BSP (binary space partitioning)</a> : + It's a very fast algorithm that allows you to split space with segments. You will define a segment and the algorithm will sort if an object is in front of this segment or behind. + It's very useful with a map, city, dungeon... The segments can be created at the same time if you generate a map and can be fast forward. + <img src="/img/guest/2021/Frustum_culling/BSP.png" alt="BSP example"/> + - Lot of other methods exist, be curious. + I don't implement each of these methods, I just learn it to know that they exist if one day I need specific space partitioning. + Some algorithm is great to parallelize like octree of quadtree if you use multithread and must also balance on your decision. + </p> + + <h3>Compute shader</h3> + <p> + Compute shader allows you to process computation on shader. + This technique must be used only if you have a high parallelized task like check collision with a simple list of bounds. + I never implemented this technique for the frustum culling but it can be used in this case to avoid updating space partitioning if you have a lot of objects that move. + </p> + + <h2>Additional resources</h2> + <ul> + <li><a href="http://www.cs.otago.ac.nz/postgrads/alexis/planExtraction.pdf" target="_blank"> + Article about camera frustum extraction</a>: Fast Extraction of Viewing Frustum Plans from the WorldView-Projection Matrix by Gil Gribb and Klaus Hartmann</li> + + <li><a href="https://gdbooks.gitbooks.io/3dcollisions/content/Chapter1/aabb.html" target="_blank"> + Article about collisions detection</a>: A wonderful resource for collision detection and another approach about volume, culling and mathematic concept</li> + + <li><a href="https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/frustum-culling-r4613/" target="_blank"> + Article to go further</a>: A good article to go further on GPU culling process, multithreading and OBB</li> + </ul> + + + <author> + <strong>Article by: </strong>Six Jonathan<br> + <strong>Contact: </strong><a href="Six-Jonathan@orange.fr" target="_blank">e-mail</a><br> + <strong>Date: </strong> 09/2021<br> + <div> + <a href="https://github.com/Renardjojo"> + <svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle"> + <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path> + </svg> + </a> + <a href="https://www.linkedin.com/in/jonathan-six-4553611a9/"> + <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 34 34" class="global-nav__logo"> + <path d="M34,2.5v29A2.5,2.5,0,0,1,31.5,34H2.5A2.5,2.5,0,0,1,0,31.5V2.5A2.5,2.5,0,0,1,2.5,0h29A2.5,2.5,0,0,1,34,2.5ZM10,13H5V29h5Zm.45-5.5A2.88,2.88,0,0,0,7.59,4.6H7.5a2.9,2.9,0,0,0,0,5.8h0a2.88,2.88,0,0,0,2.95-2.81ZM29,19.28c0-4.81-3.06-6.68-6.1-6.68a5.7,5.7,0,0,0-5.06,2.58H17.7V13H13V29h5V20.49a3.32,3.32,0,0,1,3-3.58h.19c1.59,0,2.77,1,2.77,3.52V29h5Z" fill="currentColor"></path> + </svg> + </a> + </div> + </author> + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/2021/Scene/Scene-Graph.html b/pub/Guest-Articles/2021/Scene/Scene-Graph.html @@ -0,0 +1,774 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Scene Graph</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/2021/Scene/Scene-Graph</h1> +<p> + In this article, we will talk about the <def>scene graph</def>. + A scene graph is not a class or an object, it's more like a pattern that allows you to create inheritance. + This pattern is used a lot in game engines. + For example, it is used in animation to manage bones. + If I move my arm, my hand needs to move too. + To obtain this result, I need a hierarchy with parents and children. + Thanks to it, my hand will be a child of my arm that is also a child of my body. + This hierarchy has the consequence of representing objects in global and local space. + Global space is based on position/rotation and scale based on the world and local space is based on its parent. + So, if I only move my hand, its local and global position will change but not the local and global position of my arm. + However, if I move my arm, its local and global position will change but also the global position of my hand. + Scene graphs can take a lot of forms: register or not the parent, list or contiguous buffer to store the children, flags... + Firstly, we will see a quick example of it. + In a second time, we will see simple optimizations to improve it. + So, talk less, code more ! ;D + </p> + + <div class="box"> + <img src="/img/guest/2021/scene_graph/graph.png" width="400" style="display: inline" alt="Graph example"/> + <img src="/img/guest/2021/scene_graph/sceneGraph.png" width="400" style="display: inline" alt="Scene graph example in editor"/> + </div> + + <p> + First of all, let’s create a transform. + Transform will contain local and global space information about our entities + </p> + + <pre><code> +struct Transform +{ + /*SPACE INFORMATION*/ + //Local space information + glm::vec3 pos = { 0.0f, 0.0f, 0.0f }; + glm::vec3 eulerRot = { 0.0f, 0.0f, 0.0f }; + glm::vec3 scale = { 1.0f, 1.0f, 1.0f }; + + //Global space information concatenate in matrix + glm::mat4 modelMatrix = glm::mat4(1.0f); +}; + </code></pre> + + <note> + In this example, we use the Euler angle system. + The Euler system is easy to use and understand but it contains a lot of undesirable effects: <a href="https://en.wikipedia.org/wiki/Gimbal_lock" target="_blank">Gimbal lock</a>, shortest angle between to angle thanks to lerp is wrong, cost and size... + To avoid it, we need to use <strong>quaternion</strong> but it needs to be hidden because it's hard to represent a rotation thanks to it. + To have a better understanding of this tutorial we will use the Euler angle but I invite you to do some research on quaternion. + </note> + + <p> + Now we can represent local and global space information about an object. + We will make create a class called <funct>Entity</funct>. + This class will simply wrap the space information with a model for the visual demonstration. + </p> + + <pre><code> +class Entity : public Model +{ +public: +[...] + +Transform transform; + + // constructor, expects a filepath to a 3D model. + Entity(string const& path, bool gamma = false) : Model(path, gamma) + {} +}; + </code></pre> + <p> + The scene graph is still missing. So, let's add it into the Entity class. + </p> + <pre><code> +/*SCENE GRAPH*/ +std::list<std::unique_ptr<Entity>> children; + Entity* parent = nullptr; + </code></pre> + <p> + As we said, scene graphs can take a lot of forms: use std::vector instead of std::list, don't register its parent to simplify the size of the class... + In our example we used std::list. + We don't want our entity address to change at all. + My parents’ address always needs to be valid. + Then, I used std::unique_ptr because it's the cleanest approach to avoid a leak. + Memory leak appears happens if you allocate memory thanks to new or malloc and forget to call delete or free when datas disappears. + Information is still in memory but you cannot have access to it. + If you want more information about it, many tutorials talk about it more clearly. + </p> + + <p> + <dl> + <dt> + We need now to create a function to add a child to our entity. + This function will be easy: + </dt> + <dd> + 1: Add entity + </dd> + <dd> + 2: Register parent of this new entity as the current entity + </dd> + </dl> + </p> + + + <pre><code> +template<typename... TArgs> +void addChild(const TArgs&... args) +{ + children.emplace_back(std::make_unique<Entity>(args...)); + children.back()->parent = this; +} + </code></pre> + + <note> + A variadic template allows us to create an entity with any constructor that you want to use without overloading the addChild function. + I used it in this example to awake your curiosity and show you a simple use of it. + </note> + + <p> + Ok, perfect! It's almost done, courage! + Now we have scene graphs and space information but something is still missing. + When we want to send space information to our shader, we need a model matrix. + However, we don't see how to compute it thanks to our parents and our local information. + First, we need a function to create a local model matrix that represents an object in space based on its parent. + This function will be added to the transform class. + </p> + + <pre><code> +glm::mat4 getLocalModelMatrix() +{ + const glm::mat4 transformX = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(eulerRot.x), + glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::mat4 transformY = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(eulerRot.y), + glm::vec3(0.0f, 1.0f, 0.0f)); + const glm::mat4 transformZ = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(eulerRot.z), + glm::vec3(0.0f, 0.0f, 1.0f)); + + // Y * X * Z + const glm::mat4 roationMatrix = transformY * transformX * transformZ; + + // translation * rotation * scale (also know as TRS matrix) + return <function id='55'>glm::translate</function>(glm::mat4(1.0f), pos) * + roationMatrix * + <function id='56'>glm::scale</function>(glm::mat4(1.0f), scale); +} + </code></pre> + + <p> + To combine multiple model matrices, we need to multiply them. + So if I multiply the local model matrix of my hand with the model matrix of the world with arm and arm with hand, I will obtain the global matrix of my hand! + So, let's implement a function in entity class that will do it for us: + </p> + + <pre><code> +void updateSelfAndChild() +{ + if (parent) + modelMatrix = parent->modelMatrix * getLocalModelMatrix(); + else + modelMatrix = getLocalModelMatrix(); + + for (auto&& child : children) + { + child->updateSelfAndChild(); + } +} + </code></pre> + + <p> + Now, if I update the world, all of my entities contained will also be updated and our global model matrix will be computed. + Finally, we just need to add some code in the main function to add multiple moons with the same local distance and scale and code in the main loop to move the first entity. + </p> + + <pre><code> +/*BEFOR THE MAIN LOOP*/ + +// load entities +// ----------- +const char* pathStr = "resources/objects/planet/planet.obj"; +Entity ourEntity(FileSystem::getPath(pathStr)); +ourEntity.transform.pos.x = 10; +const float scale = 0.75; +ourEntity.transform.scale = { scale, scale, scale }; + +{ + Entity* lastEntity = &ourEntity; + + for (unsigned int i = 0; i &lt; 10; ++i) + { + lastEntity->addChild(FileSystem::getPath(pathStr)); + lastEntity = lastEntity->children.back().get(); + + //Set tranform values + lastEntity->transform.pos.x = 10; + lastEntity->transform.scale = { scale, scale, scale }; + } +} +ourEntity.updateSelfAndChild(); + +/*IN THE MAIN LOOP*/ + +// draw our scene graph +Entity* lastEntity = &ourEntity; +while (lastEntity->children.size()) +{ + ourShader.setMat4("model", lastEntity->transform.modelMatrix); + lastEntity->Draw(ourShader); + lastEntity = lastEntity->children.back().get(); +} + +ourEntity.transform.eulerRot.y += 20 * deltaTime; +ourEntity.updateSelfAndChild(); + </code></pre> + + <p> + We can see this result thanks to this code. + </p> + + <img src="/img/guest/2021/scene_graph/result.png" alt="Graph example"/> + + <h2>Optimization</h2> + + <p> + Being pragmatic is essential to implement a new feature but now let's see how to optimize it. + Firstly, in the main loop, we always update the model matrix even if it doesn't move. + In programming, a pattern called a dirty flag can help us. + A dirty flag is a simple boolean called "isDirty" that allows us to know if an entity was moved during the previous frame. + So, if the user scales, rotates or translates it, we need to set this flag on. + Don't forget, the model matrix is based on the parent model matrix. + So if I change it, I also need to compute all its children's matrix. + This flag will be set off in the update function when the model matrix was recomputed. + </p> + + <warning> + This pattern is very powerful but can be integrated easily only if you encapsulate your class correctly. + I haven’t done it to give you an example of rigid code without the possibility to evolve and change easily. + This code requires the programmer to code its functionality perfectly and is difficult to change, improve or optimize. + But in production, time is a precious resource and you sometimes need to implement the feature without optimizing. + One day, if your feature becomes the performance bottleneck, you need to change it easily. + Encapsulation, private/protected, getter/setter is C++ advantage that allows you to do it. + Encapsulation has its downsides too. + It can increase the size of your class and reduce its visibility. + It can also be laborious if you create a getter/setter for all your members. + An illarouse <a href="https://www.youtube.com/watch?v=-AQfQFcXac8" target="_blank">video</a> show the c++ limit but keep in mind this example and make do the balance in function of your situation. + </warning> + + <p> + Let's remake do again transform function with encapsulation and dirty flag + </p> + + <pre><code> +class Transform +{ +protected: + //Local space information + glm::vec3 m_pos = { 0.0f, 0.0f, 0.0f }; + glm::vec3 m_eulerRot = { 0.0f, 0.0f, 0.0f }; //In degrees + glm::vec3 m_scale = { 1.0f, 1.0f, 1.0f }; + + //Global space information concatenate in matrix + glm::mat4 m_modelMatrix = glm::mat4(1.0f); + + //Dirty flag + bool m_isDirty = true; + +protected: + glm::mat4 getLocalModelMatrix() + { + const glm::mat4 transformX = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(m_eulerRot.x), + glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::mat4 transformY = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(m_eulerRot.y), + glm::vec3(0.0f, 1.0f, 0.0f)); + const glm::mat4 transformZ = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), + <function id='63'>glm::radians</function>(m_eulerRot.z), + glm::vec3(0.0f, 0.0f, 1.0f)); + + // Y * X * Z + const glm::mat4 roationMatrix = transformY * transformX * transformZ; + + // translation * rotation * scale (also know as TRS matrix) + return <function id='55'>glm::translate</function>(glm::mat4(1.0f), m_pos) * + roationMatrix * + <function id='56'>glm::scale</function>(glm::mat4(1.0f), m_scale); + } +public: + + void computeModelMatrix() + { + m_modelMatrix = getLocalModelMatrix(); + } + + void computeModelMatrix(const glm::mat4& parentGlobalModelMatrix) + { + m_modelMatrix = parentGlobalModelMatrix * getLocalModelMatrix(); + } + + void setLocalPosition(const glm::vec3& newPosition) + { + m_pos = newPosition; + m_isDirty = true; + } + + [...] + + const glm::vec3& getLocalPosition() + { + return m_pos; + } + + [...] + + const glm::mat4& getModelMatrix() + { + return m_modelMatrix; + } + + bool isDirty() + { + return m_isDirty; + } +}; + + +class Entity : public Model +{ +public: + //Scene graph + std::list<std::unique_ptr<Entity>> children; + Entity* parent = nullptr; + + //Space information + Transform transform; + + // constructor, expects a filepath to a 3D model. + Entity(string const& path, bool gamma = false) : Model(path, gamma) + {} + + //Add child. Argument input is argument of any constructor that you create. + //By default you can use the default constructor and don't put argument input. + template<typename... TArgs> + void addChild(const TArgs&... args) + { + children.emplace_back(std::make_unique<Entity>(args...)); + children.back()->parent = this; + } + + //Update transform if it was changed + void updateSelfAndChild() + { + if (!transform.isDirty()) + return; + + forceUpdateSelfAndChild(); + } + + //Force update of transform even if local space don't change + void forceUpdateSelfAndChild() + { + if (parent) + transform.computeModelMatrix(parent->transform.getModelMatrix()); + else + transform.computeModelMatrix(); + + for (auto&& child : children) + { + child->forceUpdateSelfAndChild(); + } + } +}; + </code></pre> + + <p> + Perfect! Another solution to improve performance can be memorizing the local Model matrix. + Thanks to it, we avoid recomputing all child local model matrices if the parent is moved. + This solution improves performance but transforms become heavier. + This size is really important for your hardware. + We will see why in the limitation subchapter. + You can find the code <a href="https://learnopengl.com/code_viewer_gh.php?code=src/8.guest/2021/1.scene/1.scene_graph/scene_graph.cpp" target="_blank">here</a>. + </p> + + <h2>Limitation</h2> + + <p> + Do you ever hear about <def>data oriented design</def> ? No ? Let's talk brevely about it ! + </p> + <p> + This design is based on how your hardware works. + In your computer, data is aligned into your memory. + When you use data like a variable, this data is sent to the cache. + The cache is a very fast memory but without a lot of space. + This memory is localized into your CPU. + For example, if you want to make a cake, you need to go to a supermarket (hard disk) to buy ingredients. + A supermarket is very big but is also very far from your home. + When you buy your ingredients, you store them in your fridge (RAM). + Your fridge contains ingredients that you need to live but I hope for you that you don't live only with cake ^^ + It is also small but near to your kitchen worktop. + And finally, your kitchen worktop (the cache) is small and can't contain all your ingredients but is very near to your preparation. + It's the same for the PC! + When you need to process data, your system will copy your data but also all datas after in cache (depending on your cache line size). + So, if all your datas are not contiguous in your memory, you will be as slow as if you need to take eggs one by one in your fridge to make your cake. + std::list is a noncontiguous array. + std::map will probably make the job better but inheritance cannot be aligned into your memory because it's a tree. + So, if you want to make RTS for example, with an independent unit, you probably don't need or don't want a scene graph to manage your entities. + </p> + + <note> + std::map is tree and is aligned in memory but scene graph will jump into this memory and will be slower than don't use inheritance. + </note> + + <p> + You know now what a Scene graph is and how to use it with its limitations. + In the next chapter, we will talk about frustum culling with a scene graph. + </p> + + <h2>Additional resources</h2> + + <ul> + <li><a href="https://walterkuppens.com/post/wtf-is-a-scene-graph/" target="_blank"> + walterkuppens article about scene graph</a>: An article by Walter Kuppens with another approach of the scene graph.</li> + + <li><a href="https://webglfundamentals.org/webgl/lessons/webgl-scene-graph.html" target="_blank"> + webglfundamentals article and demonstration about scene graph</a>: An article with animated webGL code and animated demonstration</li> + </ul> + + <author> + <strong>Article by: </strong>Six Jonathan<br> + <strong>Contact: </strong><a href="Six-Jonathan@orange.fr" target="_blank">e-mail</a><br> + <strong>Date: </strong> 09/2021<br> + <div> + <a href="https://github.com/Renardjojo"> + <svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle"> + <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path> + </svg> + </a> + <a href="https://www.linkedin.com/in/jonathan-six-4553611a9/"> + <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 34 34" class="global-nav__logo"> + <path d="M34,2.5v29A2.5,2.5,0,0,1,31.5,34H2.5A2.5,2.5,0,0,1,0,31.5V2.5A2.5,2.5,0,0,1,2.5,0h29A2.5,2.5,0,0,1,34,2.5ZM10,13H5V29h5Zm.45-5.5A2.88,2.88,0,0,0,7.59,4.6H7.5a2.9,2.9,0,0,0,0,5.8h0a2.88,2.88,0,0,0,2.95-2.81ZM29,19.28c0-4.81-3.06-6.68-6.1-6.68a5.7,5.7,0,0,0-5.06,2.58H17.7V13H13V29h5V20.49a3.32,3.32,0,0,1,3-3.58h.19c1.59,0,2.77,1,2.77,3.52V29h5Z" fill="currentColor"></path> + </svg> + </a> + </div> + </author> + + + + + </div> + + </main> +</body> +</html> diff --git a/pub/Guest-Articles/How-to-publish.html b/pub/Guest-Articles/How-to-publish.html @@ -0,0 +1,387 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">How to publish</h1> +<h1 id="content-url" style='display:none;'>Guest-Articles/How-to-publish</h1> +<p> +If you'd like to write your own article and have it published on LearnOpenGL for everyone to read you can send over your article to me by <a href="mailto:joey.d.vries@gmail.com">e-mail</a>. By having interested readers publish their own articles on LearnOpenGL I hope this website will be more than just an online book, but a more centralized knowledge platform for everything OpenGL. +</p> + +<p> +If you'd like to publish an article you'll want to follow the following guidelines: +</p> + +<ul> +<li>Article should be OpenGL related. Articles may be technical e.g. <em>How to use <function id='1'>glDrawArrays</function>Indirect and compute shaders to efficiently render large scenes</em>, or sementical e.g. <em>How I learned OpenGL in 30 days</em>.</li> +<li>Article needs to have a positive impact on LearnOpenGL e.g. be educational, informative, inspirational.</li> +<li>Article should be written in a style similar to LearnOpenGL: a more step-by-step approach to writing tutorials.</li> +<li>When using shaders and/or a camera, make sure to use LearnOpenGL's shader and camera includes for consistency with all the other articles. Similarly, use GLFW and GLAD.</li> +<li>Article should use HTML tags to organize its layout. There is a source page you can find <a href="https://learnopengl.com/demo/example_page.txt" target="_blank">here</a> as an example (replace <code>.txt</code> extension with <code>.html</code> to view as HTML). Additionally, copy this <a href="https://learnopengl.com/demo/layout.css" target="_blank">example CSS</a> file in the same folder to test with similar styling/layout.</li> + <li>Add <code>target="_blank"</code> to your URLs/links (see example page) so pages open in a new tab by default.</li> + <li>When using the <code>&lt;</code> or <code>&gt;</code> symbol it may break the page as these are used for HTML tags. Use <b>&#38lt;</b> and <b>&#38gt;</b> instead. </li> +<li>Images/videos can be referenced by URL. If you'd like to use locally hosted images/videos, send them alongside your article and reference them as `/img/guest/&lt;year&gt;/&lt;your_article_name&gt;/&lt;image.png&gt;` with `&lt;your_article_name&gt;` in lowercase and seperated by underlines.</li> + <li>Images should not be wider than 800px, height can be anything.</li> +<li>When displaying code in your article, make sure to use spaces over tabs (4 spaces per tab).</li> +<li>If the article is technical, try to keep the code as self-contained (and as few files) as possible. And make the full source code available.</li> +<li>It's perfectly fine to have your article be a series of articles e.g. <em>Terrain rendering tutorial part X/5</em>.</li> + <li>When referencing original LearnOpenGL chapters, try to link to them and use the word <em>Chapter</em> over article/tutorial to keep that online 'book' feeling.</li> +<li>Article should of course be technically and mathematically sound.</li> +<li>Preferably keep the article name short so it fits in the sidebar :)</li> +<li>Check with me first before you start as someone else may already be working on the same topic or the topic doesn't match well.</li> +</ul> + +<p> +As I intend to keep the quality-bar high, I'll be thorough in enforcing these restrictions. This means there's a good chance I'll reject your article, or return it with a significant amount of feedback. Nevertheless, if you followed the guidelines and generally make for an interesting article that adds something on top of the already existing content it's likely your article ends up on LearnOpenGL. +</p> + +<p> +When you submit an article, please mention your full name, and a link to your website/blog if you'd like the additional exposure. +</p> + +<h2>Topic suggestions</h2> +<p> + If you'd like to contribute but don't know what to write about, the following topics are a selection of highly requested topics: +</p> + +<ul> + <li>Casaded Shadow Mapping</li> + <li>Depth of Field</li> + <li>Motion Blur</li> + <li>Temporal AA</li> + <li>Compute Shaders</li> + <li>OpenGL 4+ features</li> + <li>Tesselation Shaders</li> + <li>Reflection Probes (and in-between blending)</li> + <li>Heightmaps (with dynamic LOD-ing using quadtree or octree, and layers for grass/mud/sand/rock textures)</li> + <li>Lens flares</li> + <li>Decals</li> + <li>GPU particle effects</li> + <li>Global Illumination</li> + <li>Light Adaptation</li> + <li>HBAO+</li> + <li>Water Simulation</li> + <li>Forward+ Rendering</li> + <li>GPU programming, creating optimized shaders</li> + <li>Procedural clouds/sky</li> +</ul> + + </div> + + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Audio.html b/pub/In-Practice/2D-Game/Audio.html @@ -0,0 +1,471 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Audio</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Audio</h1> +<p> + The game's making great progress, but it still feels a bit empty as there's no audio whatsoever. In this chapter we're going to fix that. +</p> + +<p> + OpenGL doesn't offer us any support for audio capabilities (like many other aspects of game development). We have to manually load audio files into a collection of bytes, process and convert them to an audio stream, and manage multiple audio streams appropriately for use in our game. This can get complicated pretty quick and requires some low-level knowledge of audio engineering. +</p> + +<p> + If it is your cup of tea then feel free to manually load audio streams from one or more audio file extensions. We are, however, going to make use of a library for audio management called <strong>irrKlang</strong>. +</p> + +<h2>Irrklang</h2> + +<img src="/img/in-practice/breakout/irrklang.png" class="right" alt="Irrklang logo"/> +<p> + IrrKlang is a high level 2D and 3D cross platform (Windows, Mac OS X, Linux) sound engine and audio library that plays WAV, MP3, OGG, and FLAC files to name a few. It also features several audio effects like reverb, delay, and distortion that can be extensively tweaked. +</p> + +<note> + 3D audio means that an audio source can have a 3D position that will attenuate its volume based on the camera's distance to the audio source, making it feel natural in a 3D world (think of gunfire in a 3D world; most often you'll be able to hear where it came from just by the direction/location of the sound). +</note> + +<p> + IrrKlang is an easy-to-use audio library that can play most audio files with just a few lines of code, making it a perfect candidate for our Breakout game. Note that irrKlang has a slightly restrictive license: you are allowed to use irrKlang as you see fit for non-commercial purposes, but you have to pay for their pro version whenever you want to use irrKlang commercially. +</p> + +<p> + You can download irrKlang from their <a href="http://www.ambiera.com/irrklang/downloads.html" target="_blank">download</a> page; we're using version 1.5 for this chapter. Because irrKlang is closed-source, we cannot compile the library ourselves so we'll have to do with whatever irrKlang provided for us. Luckily they have plenty of precompiled library files. +</p> + +<p> + Once you include the header files of irrKlang, add their (64-bit) library (<code>irrKlang.lib</code>) to the linker settings, and copy the dll file(s) to the appropriate locations (usually the same location where the <code>.exe</code> resides) we're set to go. Note that if you want to load MP3 files, you'll also have to include the <code>ikpMP3.dll</code> file. +</p> + +<h3>Adding music</h3> +<p> + Specifically for this game I created a small little audio track so the game feels a bit more alive. You can find the audio track <a href="/audio/in-practice/breakout/breakout.mp3" target="_blank">here</a> that we'll use as the game's background music. This track is what we'll play whenever the game starts and that continuously loops until the player closes the game. Feel free to replace it with your own tracks or use it in any way you like. +</p> + +<audio controls> + <source src="/audio/in-practice/breakout/breakout.mp3" type="audio/mpeg"> + Your browser does not support the audio element. +</audio> + +<p> + Adding this to the Breakout game is extremely easy with the irrKlang library. We include the irrKlang header file, create an <code>irrKlang::ISoundEngine</code>, initialize it with <fun>createIrrKlangDevice</fun>, and then use the engine to load and play audio files: +</p> + +<pre><code> +#include &lt;irrklang/irrKlang.h&gt; +using namespace irrklang; + +ISoundEngine *SoundEngine = createIrrKlangDevice(); + +void Game::Init() +{ + [...] + SoundEngine-&gt;play2D("audio/breakout.mp3", true); +} +</code></pre> + + +<p> + Here we created a <var>SoundEngine</var> that we use for all audio-related code. Once we've initialized the sound engine, all we need to do to play audio is simply call its <fun>play2D</fun> function. Its first parameter is the filename, and the second parameter whether we want the file to loop (play again once it's finished). +</p> + +<p> + And that is all there is to it! Running the game should now cause your speakers (or headset) to violently blast out sound waves. +</p> + +<h3>Adding sounds</h3> +<p> + We're not there yet, since music by itself is not enough to make the game as great as it could be. We want to play sounds whenever something interesting happens in the game, as extra feedback to the player. Like when we hit a brick, or when we activate a powerup. Below you can find all the sounds we're going to use (courtesy of freesound.org): +</p> + +<p> + <a href="/audio/in-practice/breakout/bleep.mp3" target="_blank"><strong>bleep.mp3</strong></a>: the sound for when the ball hit a non-solid block. +</p> + +<audio controls> + <source src="/audio/in-practice/breakout/bleep.mp3" type="audio/mpeg"> + Your browser does not support the audio element. +</audio> + +<p> + <a href="/audio/in-practice/breakout/solid.wav" target="_blank"><strong>solid.wav</strong></a>: the sound for when the ball hit a solid block. +</p> + +<audio controls> + <source src="/audio/in-practice/breakout/solid.wav" type="audio/mpeg"> + Your browser does not support the audio element. +</audio> + + <p> + <a href="/audio/in-practice/breakout/powerup.wav" target="_blank"><strong>powerup.wav</strong></a>: the sound for when we the player paddle collided with a powerup block. +</p> + +<audio controls> + <source src="/audio/in-practice/breakout/powerup.wav" type="audio/mpeg"> + Your browser does not support the audio element. +</audio> + +<p> + <a href="/audio/in-practice/breakout/bleep.wav" target="_blank"><strong>bleep.wav</strong></a>: the sound for when we the ball bounces of the player paddle. +</p> + +<audio controls> + <source src="/audio/in-practice/breakout/bleep.wav" type="audio/mpeg"> + Your browser does not support the audio element. +</audio> + +<p> + Wherever a collision occurs, we play the corresponding sound. I won't walk through each of the lines of code where this is supposed to happen, but simply list the updated game code <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/9.game.cpp" target="_blank">here</a>. You should easily be able to add the sound effects at their appropriate locations. +</p> + +<p> + Putting it all together gives us a game that feels a lot more complete. All together it looks (and sounds) like this: +</p> + +<div class="video"> + <video width="600" height="450" controls> + <source src="/video/in-practice/breakout/audio.mp4" type="video/mp4" /> + </video> +</div> + +<p> + IrrKlang allows for much more fine-grained control of audio controls like advanced memory management, audio effects, or sound event callbacks. Check out their simple C++ <a href="http://www.ambiera.com/irrklang/tutorials.html" target="_blank">tutorials</a> and try to experiment with its features. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Breakout.html b/pub/In-Practice/2D-Game/Breakout.html @@ -0,0 +1,408 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Breakout</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Breakout</h1> +<p> + Over these chapters we learned a fair share about OpenGL's inner workings and how we can use them to create fancy graphics. However, aside from a lot of tech demos, we haven't really created a practical application with OpenGL. This is the introduction of a larger series about creating a relatively simple 2D game using OpenGL. The next chapters will demonstrate how we can use OpenGL in a larger, more complicated, setting. Note that the series does not necessarily introduce new OpenGL concepts but more or less show how we can apply these concepts to a larger whole. +</p> + +<p> + Because we rather keep things simple, we're going to base our 2D game on an already existing 2D arcade game. Introducing <def>Breakout</def>, a classic 2D game released in 1976 on the Atari 2600 console. Breakout requires the player, who controls a small horizontal paddle, to destroy all the bricks by bouncing a small ball against each brick without allowing the ball to reach the bottom edge. Once the player destroys all bricks, he completes the game. +</p> + +<p> + Below we can see how Breakout originally looked on the Atari 2600: +</p> + +<img src="/img/in-practice/breakout/breakout2600.png" class="medium" alt="Image of Atari 2600's Breakout"/> + +<p> + The game has the following mechanics: +</p> + +<ul> + <li>A small paddle is controlled by the player and can only move horizontally within the bounds of the screen.</li> + <li>The ball travels across the screen and each collision results in the ball changing its direction based on where it hit; this applies to the screen bounds, the bricks, and the paddle.</li> + <li>If the ball reaches the bottom edge of the screen, the player is either game over or loses a life.</li> + <li>As soon as a brick touches the ball, the brick is destroyed.</li> + <li>The player wins as soon as all bricks are destroyed.</li> + <li>The direction of the ball can be manipulated by how far the ball bounces from the paddle's center.</li> +</ul> + +<p> + Because from time to time the ball may find a small gap reaching the area above the brick wall, it will continue to bounce up and forth between the top edge of the level and the top edge of the brick layer. The ball keeps this up, until it eventually finds a gap again. This is logically where the game obtained its name from, since the ball has to <em>break out</em>. +</p> + +<h1>OpenGL Breakout</h1> +<p> + We're going to take this classic arcade game as the basis of a 2D game that we'll completely implement with OpenGL. This version of Breakout will render its graphics on the GPU which gives us the ability to enhance the classical Breakout game with some nice extra features. +</p> + +<p> + Other than the classic mechanics, our version of Breakout will feature: +</p> + +<ul> + <li>Amazing graphics!</li> + <li>Particles</li> + <li>Text rendering</li> + <li>Power-ups</li> + <li>Postprocessing effects</li> + <li>Multiple (customizable) levels</li> +</ul> + +<p> + To get you excited you can see what the game will look like after you've finished these chapters: +</p> + +<img src="/img/in-practice/breakout/cover.png" alt="OpenGL version of Breakout"/> + +<p> + These chapters will combine a large number of concepts from previous chapters and demonstrate how they can work together as a whole. Therefore, it is important to have at least finished the <a href="https://learnopengl.com/Getting-started/OpenGL" target="_blank">Getting started</a> chapters before working your way through these series. +</p> + +<p> + Also, several chapters will require concepts from other chapters (<def>Framebuffers</def> for example from the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">Advanced OpenGL</a> section) so where necessary, the required chapters are listed. +</p> + +<p> + If you believe you're ready to get your hands dirty then move on to the <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up" target="_blank">next</a> chapter. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Collisions/Ball.html b/pub/In-Practice/2D-Game/Collisions/Ball.html @@ -0,0 +1,522 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Ball</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Ball</h1> +<p> + At this point we have a level full of bricks and a movable player paddle. The only thing missing from the classic Breakout recipe is the ball. The objective is to let the ball collide with all the bricks until each of the destroyable bricks are destroyed, but this all within the condition that the ball is not allowed to reach the bottom edge of the screen. +</p> + +<p> + In addition to the general game object components, a ball has a radius, and an extra boolean value indicating whether the ball is <def>stuck</def> on the player paddle or it's allowed free movement. When the game starts, the ball is initially stuck on the player paddle until the player starts the game by pressing some arbitrary key. +</p> + +<p> + Because the ball is effectively a <fun>GameObject</fun> with a few extra properties it makes sense to create a <fun>BallObject</fun> class as a subclass of <fun>GameObject</fun>: +</p> + +<pre><code> +class BallObject : public GameObject +{ + public: + // ball state + float Radius; + bool Stuck; + + + BallObject(); + BallObject(glm::vec2 pos, float radius, glm::vec2 velocity, Texture2D sprite); + + glm::vec2 Move(float dt, unsigned int window_width); + void Reset(glm::vec2 position, glm::vec2 velocity); +}; +</code></pre> + +<p> + The constructor of <fun>BallObject</fun> initializes its own values, but also initializes the underlying <fun>GameObject</fun>. The <fun>BallObject</fun> class hosts a <fun>Move</fun> function that moves the ball based on its velocity. It also checks if it reaches any of the scene's edges and if so, reverses the ball's velocity: +</p> + +<pre><code> +glm::vec2 BallObject::Move(float dt, unsigned int window_width) +{ + // if not stuck to player board + if (!this-&gt;Stuck) + { + // move the ball + this-&gt;Position += this-&gt;Velocity * dt; + // check if outside window bounds; if so, reverse velocity and restore at correct position + if (this-&gt;Position.x &lt;= 0.0f) + { + this-&gt;Velocity.x = -this-&gt;Velocity.x; + this-&gt;Position.x = 0.0f; + } + else if (this-&gt;Position.x + this-&gt;Size.x &gt;= window_width) + { + this-&gt;Velocity.x = -this-&gt;Velocity.x; + this-&gt;Position.x = window_width - this-&gt;Size.x; + } + if (this-&gt;Position.y &lt;= 0.0f) + { + this-&gt;Velocity.y = -this-&gt;Velocity.y; + this-&gt;Position.y = 0.0f; + } + + } + return this-&gt;Position; +} +</code></pre> + +<p> + In addition to reversing the ball's velocity, we also want relocate the ball back along the edge; the ball is only able to move if it isn't stuck. +</p> + +<note> + Because the player is game over (or loses a life) if the ball reaches the bottom edge, there is no code to let the ball bounce of the bottom edge. We do need to later implement this logic somewhere in the game code though. +</note> + +<p> + You can find the code for the ball object below: +</p> + +<ul> + <li><strong>BallObject</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/5.1.ball_object_collisions.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/5.1.ball_object_collisions.cpp" target="_blank">code</a></li> +</ul> + +<p> + First, let's add the ball to the game. Just like the player paddle, we create a <fun>BallObject</fun> and define two constants that we use to initialize the ball. As for the texture of the ball, we're going to use an image that makes perfect sense in a LearnOpenGL Breakout game: <a href="/img/textures/awesomeface.png" target="_blank">ball texture</a>. +</p> + +<pre><code> +// Initial velocity of the Ball +const glm::vec2 INITIAL_BALL_VELOCITY(100.0f, -350.0f); +// Radius of the ball object +const float BALL_RADIUS = 12.5f; + +BallObject *Ball; + +void Game::Init() +{ + [...] + glm::vec2 ballPos = playerPos + glm::vec2(PLAYER_SIZE.x / 2.0f - BALL_RADIUS, + -BALL_RADIUS * 2.0f); + Ball = new BallObject(ballPos, BALL_RADIUS, INITIAL_BALL_VELOCITY, + ResourceManager::GetTexture("face")); +} +</code></pre> + +<p> + Then we have to update the position of the ball each frame by calling its <fun>Move</fun> function within the game code's <fun>Update</fun> function: +</p> + +<pre><code> +void Game::Update(float dt) +{ + Ball->Move(dt, this-&gt;Width); +} +</code></pre> + +<p> + Furthermore, because the ball is initially stuck to the paddle, we have to give the player the ability to remove it from its stuck position. We select the space key for freeing the ball from the paddle. This means we have to change the <fun>processInput</fun> function a little: +</p> + +<pre><code> +void Game::ProcessInput(float dt) +{ + if (this-&gt;State == GAME_ACTIVE) + { + float velocity = PLAYER_VELOCITY * dt; + // move playerboard + if (this-&gt;Keys[GLFW_KEY_A]) + { + if (Player-&gt;Position.x &gt;= 0.0f) + { + Player-&gt;Position.x -= velocity; + if (Ball-&gt;Stuck) + Ball-&gt;Position.x -= velocity; + } + } + if (this-&gt;Keys[GLFW_KEY_D]) + { + if (Player-&gt;Position.x &lt;= this-&gt;Width - Player-&gt;Size.x) + { + Player-&gt;Position.x += velocity; + if (Ball-&gt;Stuck) + Ball-&gt;Position.x += velocity; + } + } + if (this-&gt;Keys[GLFW_KEY_SPACE]) + Ball-&gt;Stuck = false; + } +} +</code></pre> + +<p> + Here, if the user presses the space bar, the ball's <var>Stuck</var> variable is set to <code>false</code>. Note that we also move the position of the ball alongside the paddle's position whenever the ball is stuck. +</p> + +<p> + Last, we need to render the ball which by now should be fairly obvious: +</p> + +<pre><code> +void Game::Render() +{ + if (this->State == GAME_ACTIVE) + { + [...] + Ball->Draw(*Renderer); + } +} +</code></pre> + +<p> + The result is a ball that follows the paddle and roams freely whenever we press the spacebar. The ball also properly bounces of the left, right, and top edge, but it doesn't yet seem to collide with any of the bricks as we can see: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/no_collisions.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/no_collisions.png" class="clean"/> + </video> +</div> + +<p> + What we want is to create one or several function(s) that check if the ball object is colliding with any of the bricks in the level and if so, destroy the brick. These so called <def>collision detection</def> functions is what we'll focus on in the <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection" target="_blank">next</a> chapter. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Collisions/Collision-detection.html b/pub/In-Practice/2D-Game/Collisions/Collision-detection.html @@ -0,0 +1,559 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Collision detection</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Collision-detection</h1> +<p> + When trying to determine if a collision occurs between two objects, we generally do not use the vertex data of the objects themselves since these objects often have complicated shapes; this in turn makes the collision detection complicated. For this reason, it is a common practice to use more simple shapes (that usually have a nice mathematical definition) for collision detection that we <em>overlay</em> on top of the original object. We then check for collisions based on these simple shapes; this makes the code easier and saves a lot of performance. A few examples of such <def>collision shapes</def> are circles, spheres, rectangles, and boxes; these are a lot simpler to work with compared to arbitrary meshes with hundreds of triangles. +</p> + +<p> + While the simple shapes do give us easier and more efficient collision detection algorithms, they share a common disadvantage in that these shapes usually do not fully surround the object. The effect is that a collision may be detected that didn't really collide with the actual object; one should always keep in mind that these shapes are just approximations of the real shapes. +</p> + +<h2>AABB - AABB collisions</h2> +<p> + AABB stands for <def>axis-aligned bounding box</def>, a rectangular collision shape aligned to the base axes of the scene, which in 2D aligns to the x and y axis. Being axis-aligned means the rectangular box has no rotation and its edges are parallel to the base axes of the scene (e.g. left and right edge are parallel to the y axis). The fact that these boxes are always aligned to the axes of the scene makes calculations easier. Here we surround the ball object with an AABB: +</p> + +<img src="/img/in-practice/breakout/collisions_ball_aabb.png" alt="AABB on top of ball in OpenGL"/> + + + +<p> + Almost all the objects in Breakout are rectangular based objects, so it makes perfect sense to use axis aligned bounding boxes for detecting collisions. This is exactly what we're going to do. +</p> + +<p> + Axis aligned bounding boxes can be defined in several ways. One of them is to define an AABB by a top-left and a bottom-right position. The <fun>GameObject</fun> class that we defined already contains a top-left position (its <var>Position</var> vector), and we can easily calculate its bottom-right position by adding its size to the top-left position vector (<var>Position</var><code> + </code><var>Size</var>). Effectively, each <fun>GameObject</fun> contains an AABB that we can use for collisions. +</p> + +<p> + So how do we check for collisions? A collision occurs when two collision shapes enter each other's regions e.g. the shape that determines the first object is in some way inside the shape of the second object. For AABBs this is quite easy to determine due to the fact that they're aligned to the scene's axes: we check for each axis if the two object' edges on that axis overlap. So we check if the horizontal edges overlap, and if the vertical edges overlap of both objects. If both the horizontal <strong>and</strong> vertical edges overlap we have a collision. +</p> + +<img src="/img/in-practice/breakout/collisions_overlap.png" class="clean" alt="Image of overlapping edges of AABB"/> + +<p> + Translating this concept to code is relatively straightforward. We check for overlap on both axes and if so, return a collision: +</p> + +<pre><code> +bool CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision +{ + // collision x-axis? + bool collisionX = one.Position.x + one.Size.x &gt;= two.Position.x && + two.Position.x + two.Size.x &gt;= one.Position.x; + // collision y-axis? + bool collisionY = one.Position.y + one.Size.y &gt;= two.Position.y && + two.Position.y + two.Size.y &gt;= one.Position.y; + // collision only if on both axes + return collisionX && collisionY; +} +</code></pre> + +<p> + We check if the right side of the first object is greater than the left side of the second object <strong>and</strong> if the second object's right side is greater than the first object's left side; similarly for the vertical axis. If you have trouble visualizing this, try to draw the edges/rectangles on paper and determine this for yourself. +</p> + +<p> + To keep the collision code a bit more organized we add an extra function to the <fun>Game</fun> class: +</p> + +<pre><code> +class Game +{ + public: + [...] + void DoCollisions(); +}; +</code></pre> + +<p> + Within <fun>DoCollisions</fun>, we check for collisions between the ball object and each brick of the level. If we detect a collision, we set the brick's <var>Destroyed</var> property to <code>true</code>, which instantly stops the level from rendering this brick: +</p> + +<pre><code> +void Game::DoCollisions() +{ + for (GameObject &box : this-&gt;Levels[this-&gt;Level].Bricks) + { + if (!box.Destroyed) + { + if (CheckCollision(*Ball, box)) + { + if (!box.IsSolid) + box.Destroyed = true; + } + } + } +} +</code></pre> + +<p> + Then we also need to update the game's <fun>Update</fun> function: +</p> + +<pre><code> +void Game::Update(float dt) +{ + // update objects + Ball->Move(dt, this->Width); + // check for collisions + this->DoCollisions(); +} +</code></pre> + + +<p> + If we run the code now, the ball should detect collisions with each of the bricks and if the brick is not solid, the brick is destroyed. If you run the game now it'll look something like this: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/collisions.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/collisions.png" class="clean"/> + </video> +</div> + +<p> + While the collision detection does work, it's not very precise since the ball's rectangular collision shape collides with most of the bricks without the ball directly touching them. Let's see if we can figure out a more precise collision detection technique. +</p> + +<h2>AABB - Circle collision detection</h2> +<p> + Because the ball is a circle-like object, an AABB is probably not the best choice for the ball's collision shape. The collision code thinks the ball is a rectangular box, so the ball often collides with a brick even though the ball sprite itself isn't yet touching the brick. +</p> + +<img src="/img/in-practice/breakout/collisions_ball_aabb_touch.png" alt="Ball colliding with brick as an AABB"/> + +<p> + It makes much more sense to represent the ball with a circle collision shape instead of an AABB. For this reason we included a <var>Radius</var> variable within the ball object. To define a circle collision shape, all we need is a position vector and a radius. +</p> + + <img src="/img/in-practice/breakout/collisions_circle.png" alt="Ball circular collision shape"/> + +<p> + This does mean we have to update the detection algorithm since it currently only works between two AABBs. Detecting collisions between a circle and a rectangle is a bit more complicated, but the trick is as follows: we find the point on the AABB that is closest to the circle, and if the distance from the circle to this point is less than its radius, we have a collision. +</p> + + <p> + The difficult part is getting this closest point \(\color{red}{\bar{P}}\) on the AABB. The following image shows how we can calculate this point for any arbitrary AABB and circle: + </p> + + <img src="/img/in-practice/breakout/collisions_aabb_circle.png" class="clean" alt="AABB - Circle collision detection"/> + +<p> + We first need to get the difference vector between the ball's center \(\color{blue}{\bar{C}}\) and the AABB's center \(\color{green}{\bar{B}}\) to obtain \(\color{purple}{\bar{D}}\). What we then need to do is <def>clamp</def> this vector \(\color{purple}{\bar{D}}\) to the AABB's half-extents \(\color{orange}{{w}}\) and \(\color{teal}{\bar{h}}\) and add it to \(\color{green}{\bar{B}}\). The half-extents of a rectangle are the distances between the rectangle's center and its edges: its size divided by two. This returns a position vector that is always located somewhere at the edge of the AABB (unless the circle's center is inside the AABB). +</p> + +<note> + A clamp operation <strong>clamps</strong> a value to a value within a given range. This is often expressed as: + +<pre><code> +float clamp(float value, float min, float max) { + return std::max(min, std::min(max, value)); +} +</code></pre> + + For example, a value of <code>42.0f</code> is clamped to <code>6.0f</code> with a range of <code>3.0f</code> to <code>6.0f</code>, and a value of <code>4.20f</code> would be clamped to <code>4.20f</code>. <br/> + + Clamping a 2D vector means we clamp both its <code>x</code> and its <code>y</code> component within the given range. +</note> + +<p> + This clamped vector \(\color{red}{\bar{P}}\) is then the closest point from the AABB to the circle. What we then need to do is calculate a new difference vector \(\color{purple}{\bar{D'}}\) that is the difference between the circle's center \(\color{blue}{\bar{C}}\) and the vector \(\color{red}{\bar{P}}\). +</p> + + <img src="/img/in-practice/breakout/collisions_aabb_circle_radius_compare.png" class="clean" alt="Calculating difference vector D' to get distance between circle and closest point AABB"/> + +<p> + Now that we have the vector \(\color{purple}{\bar{D'}}\), we can compare its length to the radius of the circle. If the length of \(\color{purple}{\bar{D'}}\) is less than the circle's radius, we have a collision. +</p> + +<p> + This is all expressed in code as follows: +</p> + +<pre><code> +bool CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision +{ + // get center point circle first + glm::vec2 center(one.Position + one.Radius); + // calculate AABB info (center, half-extents) + glm::vec2 aabb_half_extents(two.Size.x / 2.0f, two.Size.y / 2.0f); + glm::vec2 aabb_center( + two.Position.x + aabb_half_extents.x, + two.Position.y + aabb_half_extents.y + ); + // get difference vector between both centers + glm::vec2 difference = center - aabb_center; + glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents); + // add clamped value to AABB_center and we get the value of box closest to circle + glm::vec2 closest = aabb_center + clamped; + // retrieve vector between center circle and closest point AABB and check if length &lt;= radius + difference = closest - center; + return glm::length(difference) &lt; one.Radius; +} +</code></pre> + +<p> + We create an overloaded function for <fun>CheckCollision</fun> that specifically deals with the case between a <fun>BallObject</fun> and a <fun>GameObject</fun>. Because we did not store the collision shape information in the objects themselves we have to calculate them: first the center of the ball is calculated, then the AABB's half-extents and its center. +</p> + +<p> + Using these collision shape attributes we calculate vector \(\color{purple}{\bar{D}}\) as <var>difference</var> that we clamp to <var>clamped</var> and add to the AABB's center to get point \(\color{red}{\bar{P}}\) as <var>closest</var>. Then we calculate the difference vector \(\color{purple}{\bar{D'}}\) between <var>center</var> and <var>closest</var> and return whether the two shapes collided or not. +</p> + +<p> + Since we previously called <fun>CheckCollision</fun> with the ball object as its first argument, we do not have to change any code since the overloaded version of <fun>CheckCollision</fun> now automatically applies. The result is now a much more precise collision detection algorithm: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/collisions_circle.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/collisions_precise.png" class="clean"/> + </video> +</div> + +<p> + It seems to work, but still, something is off. We properly do all the collision detection, but the ball does not <strong>react</strong> in any way to the collisions. We need to update the ball's position and/or velocity whenever a collision occurs. This is the topic of the <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution" target="_blank">next</a> chapter. +</p> + + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Collisions/Collision-resolution.html b/pub/In-Practice/2D-Game/Collisions/Collision-resolution.html @@ -0,0 +1,635 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Collision resolution</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Collisions/Collision-resolution</h1> +<p> + At the end of the last chapter we had a working collision detection system. However, the ball does not react in any way to the detected collisions; it moves straight through all the bricks. We want the ball to <em>bounce</em> of the collided bricks. This chapter discusses how we can accomplish this so called <def>collision resolution</def> within the AABB - circle collision detection logic. +</p> + +<p> + Whenever a collision occurs we want two things to happen: we want to reposition the ball so it is no longer inside the other object and second, we want to change the direction of the ball's velocity so it looks like it's bouncing of the object. +</p> + +<h3>Collision repositioning</h3> +<p> + To position the ball object outside the collided AABB we have to figure out the distance the ball penetrated the bounding box. For this we'll revisit the diagram from the previous chapter: +</p> + +<img src="/img/in-practice/breakout/collisions_aabb_circle_resolution.png" class="clean" alt="Collision resolution between circle and AABB"/> + +<p> + Here the ball moved slightly into the AABB and a collision was detected. We now want to move the ball out of the shape so that it merely touches the AABB as if no collision occurred. To figure out how much we need to move the ball out of the AABB we need to retrieve the vector \(\color{brown}{\bar{R}}\), which is the level of penetration into the AABB. To get this vector \(\color{brown}{\bar{R}}\), we subtract \(\color{green}{\bar{V}}\) from the ball's radius. Vector \(\color{green}{\bar{V}}\) is the difference between closest point \(\color{red}{\bar{P}}\) and the ball's center \(\color{blue}{\bar{C}}\). +</p> + +<p> + Knowing \(\color{brown}{\bar{R}}\), we offset the ball's position by \(\color{brown}{\bar{R}}\) positioning it directly against the AABB; the ball is now properly positioned. +</p> + +<h3>Collision direction</h3> +<p> + Next we need to figure out how to update the ball's velocity after a collision. For Breakout we use the following rules to change the ball's velocity: +</p> + + <ol> + <li>If the ball collides with the right or left side of an AABB, its horizontal velocity (<code>x</code>) is reversed.</li> + <li>If the ball collides with the bottom or top side of an AABB, its vertical velocity (<code>y</code>) is reversed.</li> + </ol> + +<p> + But how do we figure out the direction the ball hit the AABB? There are several approaches to this problem. One of them is that, instead of 1 AABB, we use 4 AABBs for each brick that we each position at one of its edges. This way we can determine which AABB and thus which edge was hit. However, a simpler approach exists with the help of the dot product. +</p> + +<p> + You probably still remember from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter that the dot product gives us the angle between two normalized vectors. What if we were to define four vectors pointing north, south, west, and east, and calculate the dot product between them and a given vector? The resulting dot product between these four direction vectors and the given vector that is highest (dot product's maximum value is <code>1.0f</code> which represents a <code>0</code> degree angle) is then the direction of the vector. +</p> + +<p> + This procedure looks as follows in code: +</p> + +<pre><code> +Direction VectorDirection(glm::vec2 target) +{ + glm::vec2 compass[] = { + glm::vec2(0.0f, 1.0f), // up + glm::vec2(1.0f, 0.0f), // right + glm::vec2(0.0f, -1.0f), // down + glm::vec2(-1.0f, 0.0f) // left + }; + float max = 0.0f; + unsigned int best_match = -1; + for (unsigned int i = 0; i &lt; 4; i++) + { + float dot_product = glm::dot(glm::normalize(target), compass[i]); + if (dot_product &gt; max) + { + max = dot_product; + best_match = i; + } + } + return (Direction)best_match; +} +</code></pre> + +<p> + The function compares <var>target</var> to each of the direction vectors in the <var>compass</var> array. The compass vector <var>target</var> is closest to in angle, is the direction returned to the function caller. Here <var>Direction</var> is part of an enum defined in the game class's header file: +</p> + +<pre><code> +enum Direction { + UP, + RIGHT, + DOWN, + LEFT +}; +</code></pre> + +<p> + Now that we know how to get vector \(\color{brown}{\bar{R}}\) and how to determine the direction the ball hit the AABB, we can start writing the collision resolution code. +</p> + +<h3>AABB - Circle collision resolution</h3> +<p> + To calculate the required values for collision resolution we need a bit more information from the collision function(s) than just a <code>true</code> or <code>false</code>. We're now going to return a <def>tuple</def> of information that tells us if a collision occurred, what direction it occurred, and the difference vector \(\color{brown}{\bar{R}}\). You can find the <code>tuple</code> container in the <code>&lt;tuple&gt;</code> header. +</p> + +<p> + To keep the code slightly more organized we'll typedef the collision relevant data as <fun>Collision</fun>: +</p> + +<pre><code> +typedef std::tuple&lt;bool, Direction, glm::vec2&gt; Collision; +</code></pre> + +<p> + Then we change the code of the <fun>CheckCollision</fun> function to not only return <code>true</code> or <code>false</code>, but also the direction and difference vector: +</p> + +<pre><code> +Collision CheckCollision(BallObject &one, GameObject &two) // AABB - AABB collision +{ + [...] + if (glm::length(difference) &lt;= one.Radius) + return std::make_tuple(true, VectorDirection(difference), difference); + else + return std::make_tuple(false, UP, glm::vec2(0.0f, 0.0f)); +} +</code></pre> + +<p> + The game's <fun>DoCollision</fun> function now doesn't just check if a collision occurred, but also acts appropriately whenever a collision did occur. The function now calculates the level of penetration (as shown in the diagram at the start of this tutorial) and adds or subtracts it from the ball's position based on the direction of the collision. +</p> + +<pre><code> +void Game::DoCollisions() +{ + for (GameObject &box : this-&gt;Levels[this-&gt;Level].Bricks) + { + if (!box.Destroyed) + { + Collision collision = CheckCollision(*Ball, box); + if (std::get&lt;0&gt;(collision)) // if collision is true + { + // destroy block if not solid + if (!box.IsSolid) + box.Destroyed = true; + // collision resolution + Direction dir = std::get&lt;1&gt;(collision); + glm::vec2 diff_vector = std::get&lt;2&gt;(collision); + if (dir == LEFT || dir == RIGHT) // horizontal collision + { + Ball-&gt;Velocity.x = -Ball-&gt;Velocity.x; // reverse horizontal velocity + // relocate + float penetration = Ball-&gt;Radius - std::abs(diff_vector.x); + if (dir == LEFT) + Ball-&gt;Position.x += penetration; // move ball to right + else + Ball-&gt;Position.x -= penetration; // move ball to left; + } + else // vertical collision + { + Ball-&gt;Velocity.y = -Ball-&gt;Velocity.y; // reverse vertical velocity + // relocate + float penetration = Ball-&gt;Radius - std::abs(diff_vector.y); + if (dir == UP) + Ball-&gt;Position.y -= penetration; // move ball back up + else + Ball-&gt;Position.y += penetration; // move ball back down + } + } + } + } +} +</code></pre> + +<p> + Don't get too scared by the function's complexity since it is basically a direct translation of the concepts introduced so far. First we check for a collision and if so, we destroy the block if it is non-solid. Then we obtain the collision direction <var>dir</var> and the vector \(\color{green}{\bar{V}}\) as <var>diff_vector</var> from the tuple and finally do the collision resolution. +</p> + +<p> + We first check if the collision direction is either horizontal or vertical and then reverse the velocity accordingly. If horizontal, we calculate the penetration value \(\color{brown}R\) from the <var>diff_vector</var>'s x component and either add or subtract this from the ball's position. The same applies to the vertical collisions, but this time we operate on the <code>y</code> component of all the vectors. +</p> + +<p> + Running your application should now give you working collision resolution, but it's probably difficult to really see its effect since the ball will bounce towards the bottom edge as soon as you hit a single block and be lost forever. We can fix this by also handling player paddle collisions. +</p> + +<h2>Player - ball collisions</h2> +<p> + Collisions between the ball and the player is handled slightly different from what we've previously discussed, since this time the ball's horizontal velocity should be updated based on how far it hit the paddle from its center. The further the ball hits the paddle from its center, the stronger its horizontal velocity change should be. +</p> + +<pre><code> +void Game::DoCollisions() +{ + [...] + Collision result = CheckCollision(*Ball, *Player); + if (!Ball-&gt;Stuck && std::get&lt;0&gt;(result)) + { + // check where it hit the board, and change velocity based on where it hit the board + float centerBoard = Player-&gt;Position.x + Player-&gt;Size.x / 2.0f; + float distance = (Ball-&gt;Position.x + Ball-&gt;Radius) - centerBoard; + float percentage = distance / (Player-&gt;Size.x / 2.0f); + // then move accordingly + float strength = 2.0f; + glm::vec2 oldVelocity = Ball-&gt;Velocity; + Ball-&gt;Velocity.x = INITIAL_BALL_VELOCITY.x * percentage * strength; + Ball-&gt;Velocity.y = -Ball-&gt;Velocity.y; + Ball-&gt;Velocity = glm::normalize(Ball-&gt;Velocity) * glm::length(oldVelocity); + } +} + </code></pre> + +<p> + After we checked collisions between the ball and each brick, we'll check if the ball collided with the player paddle. If so (and the ball is not stuck to the paddle) we calculate the percentage of how far the ball's center is moved from the paddle's center compared to the half-extent of the paddle. The horizontal velocity of the ball is then updated based on the distance it hit the paddle from its center. In addition to updating the horizontal velocity, we also have to reverse the y velocity. +</p> + +<p> + Note that the old velocity is stored as <var>oldVelocity</var>. The reason for storing the old velocity is that we update the horizontal velocity of the ball's velocity vector while keeping its <code>y</code> velocity constant. This would mean that the length of the vector constantly changes, which has the effect that the ball's velocity vector is much larger (and thus stronger) if the ball hit the edge of the paddle compared to if the ball would hit the center of the paddle. For this reason, the new velocity vector is normalized and multiplied by the length of the old velocity vector. This way, the velocity of the ball is always consistent, regardless of where it hits the paddle. +</p> + +<h3>Sticky paddle</h3> +<p> + You may or may not have noticed it when you ran the code, but there is still a large issue with the player and ball collision resolution. The following shows what may happen: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/collisions_sticky_paddle.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/collisions_sticky_paddle.png" class="clean"/> + </video> +</div> + +<p> + This issue is called the <def>sticky paddle</def> issue. This happens, because the player paddle moves with a high velocity towards the ball with the ball's center ending up inside the player paddle. Since we did not account for the case where the ball's center is inside an AABB, the game tries to continuously react to all the collisions. Once it finally breaks free, it will have reversed its <code>y</code> velocity so much that it's unsure whether to go up or down after breaking free. +</p> + +<p> + We can easily fix this behavior by introducing a small hack made possible by the fact that the we can always assume we have a collision at the top of the paddle. Instead of reversing the <code>y</code> velocity, we simply always return a positive <code>y</code> direction so whenever it does get stuck, it will immediately break free. +</p> + +<pre><code> + //Ball->Velocity.y = -Ball->Velocity.y; +Ball->Velocity.y = -1.0f * abs(Ball->Velocity.y); +</code></pre> + +<p> + If you try hard enough the effect is still noticeable, but I personally find it an acceptable trade-off. +</p> + +<h3>The bottom edge</h3> +<p> + The only thing that is still missing from the classic Breakout recipe is some loss condition that resets the level and the player. Within the game class's <fun>Update</fun> function we want to check if the ball reached the bottom edge, and if so, reset the game. +</p> + +<pre><code> +void Game::Update(float dt) +{ + [...] + if (Ball->Position.y >= this->Height) // did ball reach bottom edge? + { + this->ResetLevel(); + this->ResetPlayer(); + } +} +</code></pre> + +<p> + The <fun>ResetLevel</fun> and <fun>ResetPlayer</fun> functions re-load the level and reset the objects' values to their original starting values. The game should now look a bit like this: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/collisions_complete.mp4" type="video/mp4" /> + </video> +</div> + +<p> + And there you have it, we just finished creating a clone of the classical Breakout game with similar mechanics. You can find the game class' source code here: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/5.game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/5.game.cpp" target="_blank">code</a>. + </p> + +<h2>A few notes</h2> +<p> + Collision detection is a difficult topic of video game development and possibly its most challenging. Most collision detection and resolution schemes are combined with physics engines as found in most modern-day games. The collision scheme we used for the Breakout game is a very simple scheme and one specialized specifically for this type of game. + </p> + +<p> + It should be stressed that this type of collision detection and resolution is not perfect. It calculates possible collisions only per frame and only for the positions exactly as they are at that timestep; this means that if an object would have such a velocity that it would pass over another object within a single frame, it would look like it never collided with this object. So if there are framedrops, or you reach high enough velocities, this collision detection scheme will not hold. +</p> + +<p> + Several of the issues that can still occur: +</p> + +<ul> + <li>If the ball goes too fast, it may skip over an object entirely within a single frame, not detecting any collisions.</li> + <li>If the ball hits more than one object within a single frame, it will have detected two collisions and reversed its velocity twice; not affecting its original velocity.</li> + <li>Hitting a corner of a brick could reverse the ball's velocity in the wrong direction since the distance it travels in a single frame could decide the difference between <fun>VectorDirection</fun> returning a vertical or horizontal direction.</li> +</ul> + +<p> + These chapters are however aimed to teach the readers the basics of several aspects of graphics and game-development. For this reason, this collision scheme serves its purpose; its understandable and works quite well in normal scenarios. Just keep in mind that there exist better (more complicated) collision schemes that work well in almost all scenarios (including movable objects) like the <def>separating axis theorem</def>. +</p> + +<p> + Thankfully, there exist large, practical, and often quite efficient physics engines (with timestep-independent collision schemes) for use in your own games. If you wish to delve further into such systems or need more advanced physics and have trouble figuring out the mathematics, <a href="http://box2d.org/" target="_blank">Box2D</a> is a perfect 2D physics library for implementing physics and collision detection in your applications. +</p> + + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Final-thoughts.html b/pub/In-Practice/2D-Game/Final-thoughts.html @@ -0,0 +1,375 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Final thoughts</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Final-thoughts</h1> +<p> + These last chapter gave a glimpse of what it's like to create something more than just a tech demo in OpenGL. We created a complete 2D game from scratch and learned how to abstract from certain low-level graphics concepts, use basic collision detection techniques, create particles, and we've shown a practical scenario for an orthographic projection matrix. All this using concepts we've discussed in all previous chapters. We didn't really learn new and exciting graphics techniques using OpenGL, but more as to how to combine all the knowledge so far into a larger whole. +</p> + +<p> + Creating a simple game like Breakout can be accomplished in thousands of different ways, of which this approach is just one of many. The larger a game becomes, the more you start applying abstractions and design patterns. For further reading you can find a lot more on these abstractions and design patterns in the wonderful <a href="http://gameprogrammingpatterns.com/" target="_blank">game programming patterns</a> website. +</p> + +<p> + Keep in mind that it is a difficult feat to create a game with extremely clean and well-thought out code (often close to impossible). Simply make your game in whatever way you think feels right at the time. The more you practice video-game development, the more you learn new and better approaches to solve problems. Don't let the struggle to want to create perfect code demotivate you; keep on coding! +</p> + +<h2>Optimizations</h2> +<p> + The content of these chapters and the finished game code were all focused on explaining concepts as simple as possible, without delving too much in optimization details. Therefore, many performance considerations were left out of the chapters. We'll list some of the more common improvements you'll find in modern 2D OpenGL games to boost performance for when your framerate starts to drop: +</p> + +<ul> + <li><strong>Sprite sheet / Texture atlas</strong>: instead of rendering a sprite with a single texture at a time, we combine all required textures into a single large texture (like bitmap fonts) and select the appropriate sprite texture with a targeted set of texture coordinates. Switching texture states can be expensive so a sprite sheet makes sure we rarely have to switch between textures; this also allows the GPU to more efficiently cache the texture in memory for faster lookups.</li> + <li><strong>Instanced rendering</strong>: instead of rendering a quad at a time, we could've also <def>batched</def> all the quads we want to render and then, with an <a href="https://learnopengl.com/Advanced-OpenGL/Instancing" target="_blank">instanced renderer</a>, render all the batched sprites with just a single draw call. This is relatively easy to do since each sprite is composed of the same vertices, but differs in only a model matrix; something that we can easily include in an instanced array. This allows OpenGL to render a lot more sprites per frame. Instanced rendering can also be used to render particles and/or characters glyphs. </li> + <li><strong>Triangle strips</strong>: instead of rendering each quad as two triangles, we could've rendered them with OpenGL's <var>TRIANGLE_STRIP</var> render primitive that takes only <code>4</code> vertices instead of <code>6</code>. This saves a third of the data sent to the GPU.</li> + <li><strong>Space partitioning algorithms</strong>: when checking for collisions, we compare the ball object to <strong>each</strong> of the bricks in the active level. This is a bit of a waste of CPU resources since we can easily tell that most of the bricks won't even come close to the ball within this frame. Using <def>space partitioning algorithms</def> like BSP, Octrees, or k-d trees, we partition the visible space into several smaller regions and first determine in which region(s) the ball is in. We then only check collisions between other bricks in whatever region(s) the ball is in, saving us a significant amount of collision checks. For a simple game like Breakout this will likely be overkill, but for more complicated games with more complicated collision detection algorithms this will significantly increase performance.</li> + <li><strong>Minimize state changes</strong>: state changes (like binding textures or switching shaders) are generally quite expensive in OpenGL, so you want to avoid doing a large amount of state changes. One approach to minimize state changes is to create your own state manager that stores the current value of an OpenGL state (like which texture is bound) and only switch if this value needs to change; this prevents unnecessary state changes. Another approach is to sort all the renderable objects by state change: first render all the objects with shader one, then all objects with shader two, and so on; this can of course be extended to blend state changes, texture binds, framebuffer switches etc.</li> +</ul> + +<p> + These should give you some hints as to what kind of advanced tricks we can apply to further boost the performance of a 2D game. This also gives you a glimpse of the power of OpenGL: by doing most rendering by hand we have full control over the entire process and thus also complete power over how to optimize the process. If you're not satisfied with Breakout's performance then feel free to take any of these as an exercise. +</p> + +<h2>Get creative</h2> +<p> + Now that you've seen how to create a simple game in OpenGL it is up to you to create your own rendering/game applications. Many of the techniques that we've discussed so far can be used in most 2D (and even 3D) games like sprite rendering, collision detection, postprocessing, text rendering, and particles. It is now up to you to take these techniques and combine/modify them in whichever way you think is right and develop your own handcrafted game. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Levels.html b/pub/In-Practice/2D-Game/Levels.html @@ -0,0 +1,687 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Levels</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Levels</h1> +<p> + Breakout is unfortunately not just about a single happy green face, but contains complete levels with a lot of playfully colored bricks. We want these levels to be configurable such that they can support any number of rows and/or columns, we want the levels to have solid bricks (that cannot be destroyed), we want the levels to support multiple brick colors, and we want them to be stored externally in (text) files. +</p> + +<p> + In this chapter we'll briefly walk through the code of a game level object that is used to manage a large amount of bricks. We first have to define what an actual <def>brick</def> is though. +</p> + +<p> + We create a component called a <def>game object</def> that acts as the base representation of an object inside the game. Such a game object holds state data like its position, size, and velocity. It holds a color, a rotation component, whether it is solid and/or destroyed, and it also stores a <fun>Texture2D</fun> variable as its sprite. +</p> + +<p> + Each object in the game is represented as a <fun>GameObject</fun> or a derivative of this class. You can find the code of the <fun>GameObject</fun> class below: +</p> + +<ul> + <li><strong>GameObject</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_object.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_object.cpp" target="_blank">code</a> </li> +</ul> + +<p> + A level in Breakout consists entirely of bricks so we can represent a level by exactly that: a collection of bricks. Because a brick requires the same state as a game object, we're going to represent each brick of the level as a <fun>GameObject</fun>. The declaration of the <fun>GameLevel</fun> class then looks as follows: +</p> + +<pre><code> +class GameLevel +{ +public: + // level state + std::vector&lt;GameObject&gt; Bricks; + // constructor + GameLevel() { } + // loads level from file + void Load(const char *file, unsigned int levelWidth, unsigned int levelHeight); + // render level + void Draw(SpriteRenderer &renderer); + // check if the level is completed (all non-solid tiles are destroyed) + bool IsCompleted(); +private: + // initialize level from tile data + void init(std::vector&lt;std::vector&lt;unsigned int&gt;&gt; tileData, + unsigned int levelWidth, unsigned int levelHeight); +}; +</code></pre> + +<p> + Since a level is loaded from an external (text) file, we need to propose some kind of level structure. Here is an example of what a game level may look like in a text file: +</p> + +<pre><code> +1 1 1 1 1 1 +2 2 0 0 2 2 +3 3 4 4 3 3 +</code></pre> + +<p> + A level is stored in a matrix-like structure where each number represents a type of brick, each one separated by a space. Within the level code we can then assign what each number represents. We have chosen the following representation: +</p> + +<ul> + <li>A number of 0: no brick, an empty space within the level.</li> + <li>A number of 1: a solid brick, a brick that cannot be destroyed.</li> + <li>A number higher than 1: a destroyable brick; each subsequent number only differs in color.</li> +</ul> + +<p> + The example level listed above would, after being processed by <fun>GameLevel</fun>, look like this: +</p> + +<img src="/img/in-practice/breakout/levels-example.png" class="clean" alt="Example of a level using the Breakout GameLevel class"/> + +<p> + The <fun>GameLevel</fun> class uses two functions to generate a level from file. It first loads all the numbers in a two-dimensional vector within its <fun>Load</fun> function that then processes these numbers (to create all game objects) in its <fun>init</fun> function. +</p> + + +<pre><code> +void GameLevel::Load(const char *file, unsigned int levelWidth, unsigned int levelHeight) +{ + // clear old data + this-&gt;Bricks.clear(); + // load from file + unsigned int tileCode; + GameLevel level; + std::string line; + std::ifstream fstream(file); + std::vector&lt;std::vector&lt;unsigned int&gt;&gt; tileData; + if (fstream) + { + while (std::getline(fstream, line)) // read each line from level file + { + std::istringstream sstream(line); + std::vector&lt;unsigned int&gt; row; + while (sstream &gt;&gt; tileCode) // read each word separated by spaces + row.push_back(tileCode); + tileData.push_back(row); + } + if (tileData.size() &gt; 0) + this-&gt;init(tileData, levelWidth, levelHeight); + } +} +</code></pre> + +<p> + The loaded <var>tileData</var> is then passed to the game level's <fun>init</fun> function: +</p> + +<pre><code> +void GameLevel::init(std::vector&lt;std::vector&lt;unsigned int&gt;&gt; tileData, + unsigned int lvlWidth, unsigned int lvlHeight) +{ + // calculate dimensions + unsigned int height = tileData.size(); + unsigned int width = tileData[0].size(); + float unit_width = lvlWidth / static_cast&lt;float&gt;(width); + float unit_height = lvlHeight / height; + // initialize level tiles based on tileData + for (unsigned int y = 0; y &lt; height; ++y) + { + for (unsigned int x = 0; x &lt; width; ++x) + { + // check block type from level data (2D level array) + if (tileData[y][x] == 1) // solid + { + glm::vec2 pos(unit_width * x, unit_height * y); + glm::vec2 size(unit_width, unit_height); + GameObject obj(pos, size, + ResourceManager::GetTexture("block_solid"), + glm::vec3(0.8f, 0.8f, 0.7f) + ); + obj.IsSolid = true; + this-&gt;Bricks.push_back(obj); + } + else if (tileData[y][x] &gt; 1) + { + glm::vec3 color = glm::vec3(1.0f); // original: white + if (tileData[y][x] == 2) + color = glm::vec3(0.2f, 0.6f, 1.0f); + else if (tileData[y][x] == 3) + color = glm::vec3(0.0f, 0.7f, 0.0f); + else if (tileData[y][x] == 4) + color = glm::vec3(0.8f, 0.8f, 0.4f); + else if (tileData[y][x] == 5) + color = glm::vec3(1.0f, 0.5f, 0.0f); + + glm::vec2 pos(unit_width * x, unit_height * y); + glm::vec2 size(unit_width, unit_height); + this-&gt;Bricks.push_back( + GameObject(pos, size, ResourceManager::GetTexture("block"), color) + ); + } + } + } +} +</code></pre> + +<p> + The <fun>init</fun> function iterates through each of the loaded numbers and adds a <fun>GameObject</fun> to the level's <var>Bricks</var> vector based on the processed number. The size of each brick is automatically calculated (<var>unit_width</var> and <var>unit_height</var>) based on the total number of bricks so that each brick perfectly fits within the screen bounds. +</p> + +<p> + Here we load the game objects with two new textures, a <a href="/img/in-practice/breakout/textures/block.png" target="_blank">block</a> texture and a <a href="/img/in-practice/breakout/textures/block_solid.png" target="_blank">solid block</a> texture. +</p> + +<img src="/img/in-practice/breakout/block-textures.png" alt="Image of two types of block textures"/> + +<p> + A nice little trick here is that these textures are completely in gray-scale. The effect is that we can neatly manipulate their colors within the game-code by multiplying their grayscale colors with a defined color vector; exactly as we did within the <fun>SpriteRenderer</fun>. This way, customizing the appearance of their colors doesn't look too weird or unbalanced. +</p> + +<p> + The <fun>GameLevel</fun> class also houses a few other functions, like rendering all non-destroyed bricks, or validating if all non-solid bricks are destroyed. You can find the source code of the <fun>GameLevel</fun> class below: +</p> + +<ul> + <li><strong>GameLevel</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_level.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game_level.cpp" target="_blank">code</a> </li> +</ul> + +<p> + The game level class gives us a lot of flexibility since any amount of rows and columns are supported and a user could easily create his/her own levels by modifying the level files. +</p> + +<h2>Within the game</h2> +<p> + We would like to support multiple levels in the Breakout game so we'll have to extend the game class a little by adding a vector that holds variables of type <fun>GameLevel</fun>. We'll also store the currently active level while we're at it: +</p> + +<pre><code> +class Game +{ + [...] + std::vector&lt;GameLevel&gt; Levels; + unsigned int Level; + [...] +}; +</code></pre> + +<p> + This series' version of the Breakout game features a total of 4 levels: +</p> + +<ul> + <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/one.lvl" target="_blank">Standard</a></li> + <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/two.lvl" target="_blank">A few small gaps</a></li> + <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/three.lvl" target="_blank">Space invader</a></li> + <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/levels/four.lvl" target="_blank">Bounce galore</a></li> +</ul> + +<p> + Each of the textures and levels are then initialized within the game class's <fun>Init</fun> function: +</p> + +<pre><code> +void Game::Init() +{ + [...] + // load textures + ResourceManager::LoadTexture("textures/background.jpg", false, "background"); + ResourceManager::LoadTexture("textures/awesomeface.png", true, "face"); + ResourceManager::LoadTexture("textures/block.png", false, "block"); + ResourceManager::LoadTexture("textures/block_solid.png", false, "block_solid"); + // load levels + GameLevel one; one.Load("levels/one.lvl", this-&gt;Width, this-&gt;Height / 2); + GameLevel two; two.Load("levels/two.lvl", this-&gt;Width, this-&gt;Height / 2); + GameLevel three; three.Load("levels/three.lvl", this-&gt;Width, this-&gt;Height / 2); + GameLevel four; four.Load("levels/four.lvl", this-&gt;Width, this-&gt;Height / 2); + this-&gt;Levels.push_back(one); + this-&gt;Levels.push_back(two); + this-&gt;Levels.push_back(three); + this-&gt;Levels.push_back(four); + this-&gt;Level = 0; +} +</code></pre> + +<p> + Now all that is left to do, is actually render the level. We accomplish this by calling the currently active level's <fun>Draw</fun> function that in turn calls each <fun>GameObject</fun>'s <fun>Draw</fun> function using the given sprite renderer. Next to the level, we'll also render the scene with a nice <a href="/img/in-practice/breakout/textures/background.jpg" target="_blank">background image</a> (courtesy of Tenha): +</p> + +<pre><code> +void Game::Render() +{ + if(this-&gt;State == GAME_ACTIVE) + { + // draw background + Renderer-&gt;DrawSprite(ResourceManager::GetTexture("background"), + glm::vec2(0.0f, 0.0f), glm::vec2(this-&gt;Width, this-&gt;Height), 0.0f + ); + // draw level + this-&gt;Levels[this-&gt;Level].Draw(*Renderer); + } +} +</code></pre> + +<p> + The result is then a nicely rendered level that really starts to make the game feel more alive: +</p> + +<img src="/img/in-practice/breakout/levels.png" class="clean" alt="Level in OpenGL breakout"/> + +<h3>The player paddle</h3> +<p> + While we're at it, we may just as well introduce a paddle at the bottom of the scene that is controlled by the player. The paddle only allows for horizontal movement and whenever it touches any of the scene's edges, its movement should halt. For the player paddle we're going to use the <a href="/img/in-practice/breakout/textures/paddle.png" target="_blank">following</a> texture: +</p> + +<img src="/img/in-practice/breakout/textures/paddle.png" class="clean" style="width:256px;height:auto;" alt="Texture image if a paddle in OpenGL breakout"/> + +<p> + A paddle object will have a position, a size, and a sprite texture, so it makes sense to define the paddle as a <fun>GameObject</fun> as well: +</p> + +<pre><code> +// Initial size of the player paddle +const glm::vec2 PLAYER_SIZE(100.0f, 20.0f); +// Initial velocity of the player paddle +const float PLAYER_VELOCITY(500.0f); + +GameObject *Player; + +void Game::Init() +{ + [...] + ResourceManager::LoadTexture("textures/paddle.png", true, "paddle"); + [...] + glm::vec2 playerPos = glm::vec2( + this-&gt;Width / 2.0f - PLAYER_SIZE.x / 2.0f, + this-&gt;Height - PLAYER_SIZE.y + ); + Player = new GameObject(playerPos, PLAYER_SIZE, ResourceManager::GetTexture("paddle")); +} +</code></pre> + +<p> + Here we defined several constant values that define the paddle's size and speed. Within the Game's <fun>Init</fun> function we calculate the starting position of the paddle within the scene. We make sure the player paddle's center is aligned with the horizontal center of the scene. +</p> + +<p> + With the player paddle initialized, we also need to add a statement to the Game's <fun>Render</fun> function: +</p> + +<pre><code> +Player-&gt;Draw(*Renderer); +</code></pre> + +<p> + If you'd start the game now, you would not only see the level, but also a fancy player paddle aligned to the bottom edge of the scene. As of now, it doesn't really do anything so we're going to delve into the Game's <fun>ProcessInput</fun> function to horizontally move the paddle whenever the user presses the <var>A</var> or <var>D</var> key: +</p> + +<pre><code> +void Game::ProcessInput(float dt) +{ + if (this-&gt;State == GAME_ACTIVE) + { + float velocity = PLAYER_VELOCITY * dt; + // move playerboard + if (this-&gt;Keys[GLFW_KEY_A]) + { + if (Player-&gt;Position.x &gt;= 0.0f) + Player-&gt;Position.x -= velocity; + } + if (this-&gt;Keys[GLFW_KEY_D]) + { + if (Player-&gt;Position.x &lt;= this-&gt;Width - Player-&gt;Size.x) + Player-&gt;Position.x += velocity; + } + } +} +</code></pre> + +<p> + Here we move the player paddle either in the left or right direction based on which key the user pressed (note how we multiply the velocity with the <def>deltatime</def> variable). If the paddle's <code>x</code> value would be less than <code>0</code> it would've moved outside the left edge, so we only move the paddle to the left if the paddle's <code>x</code> value is higher than the left edge's <code>x</code> position (<code>0.0</code>). We do the same for when the paddle breaches the right edge, but we have to compare the right edge's position with the right edge of the paddle (subtract the paddle's width from the right edge's <code>x</code> position). +</p> + +<p> + Now running the game gives us a player paddle that we can move all across the bottom edge: +</p> + +<img src="/img/in-practice/breakout/levels-player.png" class="clean" alt="Image of OpenGL breakout now with player paddle"/> + +<p> + You can find the updated code of the Game class here: +</p> + +<ul> + <li><strong>Game</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/4.game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/4.game.cpp" target="_blank">code</a> </li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Particles.html b/pub/In-Practice/2D-Game/Particles.html @@ -0,0 +1,630 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Particles</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Particles</h1> +<p> + A <def>particle</def>, as seen from OpenGL's perspective, is a tiny 2D quad that is always facing the camera (billboarding) and (usually) contains a texture with large parts of the sprite being transparent. A particle by itself is effectively just a sprite as we've been using extensively so far. However, when you put together hundreds or even thousands of these particles together you can create amazing effects. +</p> + +<p> + When working with particles, there is usually an object called a <def>particle emitter</def> or <def>particle generator</def> that, from its location, continuously <def>spawns</def> new particles that decay over time. If such a particle emitter would for example spawn tiny particles with a smoke-like texture, color them less bright the larger the distance from the emitter, and give them a glowy appearance, you'd get a fire-like effect: +</p> + +<img src="/img/in-practice/breakout/particles_example.jpg" alt="Example of particles as a fire"/> + +<p> + A single particle often has a life variable that slowly decays once it's spawned. Once its life is less than a certain threshold (usually <code>0</code>), we <def>kill</def> the particle so it can be replaced with a new particle when the next particle spawns. A particle emitter controls all its spawned particles and changes their behavior based on their attributes. A particle generally has the following attributes: +</p> + +<pre><code> +struct Particle { + glm::vec2 Position, Velocity; + glm::vec4 Color; + float Life; + + Particle() + : Position(0.0f), Velocity(0.0f), Color(1.0f), Life(0.0f) { } +}; +</code></pre> + +<p> + Looking at the fire example, the particle emitter probably spawns each particle with a position close to the emitter and with an upwards velocity. It seems to have 3 different regions, so it probably gives some particles a higher velocity than others. We can also see that the higher the <code>y</code> position of the particle, the less <em>yellow</em> or <em>bright</em> its color becomes. After the particles have reached a certain height, their life is depleted and the particles are killed; never reaching the stars. +</p> + +<p> + You can imagine that with systems like these we can create interesting effects like fire, smoke, fog, magic effects, gunfire residue etc. In Breakout, we're going to add a simple particle generator that follows the ball to make it all look just a bit more interesting. It'll look something like this: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/particles.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/particles.png" class="clean"/> + </video> +</div> + +<p> + Here, the particle generator spawns each particle at the ball's position, gives it a velocity equal to a fraction of the ball's velocity, and changes the color of the particle based on how long it lived. +</p> + +<p> + For rendering the particles we'll be using a different set of shaders: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec4 vertex; // &lt;vec2 position, vec2 texCoords&gt; + +out vec2 TexCoords; +out vec4 ParticleColor; + +uniform mat4 projection; +uniform vec2 offset; +uniform vec4 color; + +void main() +{ + float scale = 10.0f; + TexCoords = vertex.zw; + ParticleColor = color; + gl_Position = projection * vec4((vertex.xy * scale) + offset, 0.0, 1.0); +} +</code></pre> + +<p> + And the fragment shader: +</p> + +<pre><code> +#version 330 core +in vec2 TexCoords; +in vec4 ParticleColor; +out vec4 color; + +uniform sampler2D sprite; + +void main() +{ + color = (texture(sprite, TexCoords) * ParticleColor); +} +</code></pre> + +<p> + We take the standard position and texture attributes per particle and also accept an <var>offset</var> and a <var>color</var> uniform for changing the outcome per particle. Note that in the vertex shader we scale the particle quad by <code>10.0f</code>; you can also set the scale as a uniform and control this individually per particle. +</p> + +<p> + First, we need a list of particles that we instantiate with default <fun>Particle</fun> structs: +</p> + +<pre><code> +unsigned int nr_particles = 500; +std::vector&lt;Particle&gt; particles; + +for (unsigned int i = 0; i &lt; nr_particles; ++i) + particles.push_back(Particle()); +</code></pre> + +<p> + Then in each frame, we spawn several new particles with starting values. For each particle that is (still) alive we also update their values: +</p> + +<pre><code> +unsigned int nr_new_particles = 2; +// add new particles +for (unsigned int i = 0; i &lt; nr_new_particles; ++i) +{ + int unusedParticle = FirstUnusedParticle(); + RespawnParticle(particles[unusedParticle], object, offset); +} +// update all particles +for (unsigned int i = 0; i &lt; nr_particles; ++i) +{ + Particle &p = particles[i]; + p.Life -= dt; // reduce life + if (p.Life &gt; 0.0f) + { // particle is alive, thus update + p.Position -= p.Velocity * dt; + p.Color.a -= dt * 2.5f; + } +} +</code></pre> + +<p> + The first loop may look a little daunting. As particles die over time we want to spawn <var>nr_new_particles</var> particles each frame, but since we don't want to infinitely keep spawning new particles (we'll quickly run out of memory this way) we only spawn up to a max of <var>nr_particles</var>. If were to push all new particles to the end of the list we'll quickly get a list filled with thousands of particles. This isn't really efficient considering only a small portion of that list has particles that are alive. +</p> + +<p> + What we want is to find the first particle that is dead (life &lt; <code>0.0f</code>) and update that particle as a new respawned particle. +</p> + +<p> + The function <fun>FirstUnusedParticle</fun> tries to find the first particle that is dead and returns its index to the caller. + </p> + +<pre><code> +unsigned int lastUsedParticle = 0; +unsigned int FirstUnusedParticle() +{ + // search from last used particle, this will usually return almost instantly + for (unsigned int i = lastUsedParticle; i &lt; nr_particles; ++i) { + if (particles[i].Life &lt;= 0.0f){ + lastUsedParticle = i; + return i; + } + } + // otherwise, do a linear search + for (unsigned int i = 0; i &lt; lastUsedParticle; ++i) { + if (particles[i].Life &lt;= 0.0f){ + lastUsedParticle = i; + return i; + } + } + // override first particle if all others are alive + lastUsedParticle = 0; + return 0; +} +</code></pre> + +<p> + The function stores the index of the last dead particle it found. Since the next dead particle will most likely be right after the last particle index, we first search from this stored index. If we found no dead particles this way, we simply do a slower linear search. If no particles are dead, it will return index <code>0</code> which results in the first particle being overwritten. Note that if it reaches this last case, it means your particles are alive for too long; you'd need to spawn less particles per frame and/or reserve a larger number of particles. +</p> + +<p> + Then, once the first dead particle in the list is found, we update its values by calling <fun>RespawnParticle</fun> that takes the particle, a <fun>GameObject</fun>, and an offset vector: +</p> + +<pre><code> +void RespawnParticle(Particle &particle, GameObject &object, glm::vec2 offset) +{ + float random = ((rand() % 100) - 50) / 10.0f; + float rColor = 0.5f + ((rand() % 100) / 100.0f); + particle.Position = object.Position + random + offset; + particle.Color = glm::vec4(rColor, rColor, rColor, 1.0f); + particle.Life = 1.0f; + particle.Velocity = object.Velocity * 0.1f; +} +</code></pre> + +<p> + This function simply resets the particle's life to <code>1.0f</code>, randomly gives it a brightness (via the color vector) starting from <code>0.5</code>, and assigns a (slightly random) position and velocity based on the game object's data. +</p> + +<p> + The second particle loop within the update function loops over all particles and for each particle reduces their life by the delta time variable; this way, each particle's life corresponds to exactly the second(s) it's allowed to live multiplied by some scalar. Then we check if the particle is alive and if so, update its position and color attributes. We also slowly reduce the alpha component of each particle so it looks like they're slowly disappearing over time. +</p> + +<p> + Then what's left to do is render the particles: +</p> + +<pre><code> +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE); +particleShader.Use(); +for (Particle particle : particles) +{ + if (particle.Life &gt; 0.0f) + { + particleShader.SetVector2f("offset", particle.Position); + particleShader.SetVector4f("color", particle.Color); + particleTexture.Bind(); + <function id='27'>glBindVertexArray</function>(particleVAO); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); + <function id='27'>glBindVertexArray</function>(0); + } +} +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +</code></pre> + +<p> + Here, for each particle, we set their offset and color uniform values, bind the texture, and render the 2D quad. What's interesting to note here are the two calls to <fun><function id='70'>glBlendFunc</function></fun>. When rendering the particles, instead of the default destination blend mode of <code>GL_ONE_MINUS_SRC_ALPHA</code>, we use the <code>GL_ONE</code> (additive) blend mode that gives the particles a very neat <def>glow effect</def> when stacked onto each other. This is also likely the blend mode used when rendering the fire at the top of the chapter, since the fire is more 'glowy' at the center where most of the particles are. +</p> + +<p> + Because we (like most other parts of the Breakout chapters) like to keep things organized, we create another class called <fun>ParticleGenerator</fun> that hosts all the functionality we just described. You can find the source code below: +</p> + +<ul> + <li><a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/particle_generator.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/particle_generator.cpp" target="_blank">code</a></li> +</ul> + +<p> + Within the game code, we create a particle generator and initialize it with <a href="/img/in-practice/breakout/textures/particle.png" target="_blank">this</a> texture. +</p> + +<pre><code> +ParticleGenerator *Particles; + +void Game::Init() +{ + [...] + ResourceManager::LoadShader("shaders/particle.vs", "shaders/particle.frag", nullptr, "particle"); + [...] + ResourceManager::LoadTexture("textures/particle.png", true, "particle"); + [...] + Particles = new ParticleGenerator( + ResourceManager::GetShader("particle"), + ResourceManager::GetTexture("particle"), + 500 + ); +} +</code></pre> + +<p> + Then we change the game class's <fun>Update</fun> function by adding an update statement for the particle generator: +</p> + +<pre><code> +void Game::Update(float dt) +{ + [...] + // update particles + Particles-&gt;Update(dt, *Ball, 2, glm::vec2(Ball-&gt;Radius / 2.0f)); + [...] +} +</code></pre> + +<p> + Each of the particles will use the game object properties from the ball object, spawn 2 particles each frame, and their positions will be offset towards the center of the ball. Last up is rendering the particles: +</p> + +<pre><code> +void Game::Render() +{ + if (this-&gt;State == GAME_ACTIVE) + { + [...] + // draw player + Player-&gt;Draw(*Renderer); + // draw particles + Particles-&gt;Draw(); + // draw ball + Ball-&gt;Draw(*Renderer); + } +} +</code></pre> + +<p> + Note that we render the particles before we render the ball. This way, the particles end up rendered in front of all other objects, but behind the ball. You can find the updated game class code <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/6.game.cpp" target="_blank">here</a>. +</p> + +<p> + If you'd now compile and run your application you should see a trail of particles following the ball, just like at the beginning of the chapter, giving the game a more modern look. The system can also easily be extended to host more advanced effects, so feel free to experiment with the particle generation and see if you can come up with your own creative effects. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Postprocessing.html b/pub/In-Practice/2D-Game/Postprocessing.html @@ -0,0 +1,609 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Postprocessing</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Postprocessing</h1> +<p> + Wouldn't it be fun if we could completely spice up the visuals of the Breakout game with just a few postprocessing effects? We could create a blurry shake effect, inverse all the colors of the scene, do crazy vertex movement, and/or make use of other interesting effects with relative ease thanks to OpenGL's framebuffers. +</p> + +<note> + This chapters makes extensive use of concepts from the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> and <a href="https://learnopengl.com!Advanced-OpenGL/Anti-Aliasing" target="_blank">anti-aliasing</a> chapters. +</note> + +<p> + In the framebuffers chapter we demonstrated how we could use postprocessing to achieve interesting effects using just a single texture. In Breakout we're going to do something similar: we're going to create a framebuffer object with a multisampled renderbuffer object attached as its color attachment. All the game's render code should render to this multisampled framebuffer that then blits its content to a different framebuffer with a texture attachment as its color buffer. This texture contains the rendered anti-aliased image of the game that we'll render to a full-screen 2D quad with zero or more postprocessing effects applied. +</p> + +<p> + So to summarize, the rendering steps are: +</p> + +<ol> + <li>Bind to multisampled framebuffer.</li> + <li>Render game as normal.</li> + <li>Blit multisampled framebuffer to normal framebuffer with texture attachment.</li> + <li>Unbind framebuffer (use default framebuffer).</li> + <li>Use color buffer texture from normal framebuffer in postprocessing shader.</li> + <li>Render quad of screen-size as output of postprocessing shader.</li> +</ol> + +<p> + The postprocessing shader allows for three type of effects: shake, confuse, and chaos. +</p> + +<ul> + <li><strong>shake</strong>: slightly shakes the scene with a small blur.</li> + <li><strong>confuse</strong>: inverses the colors of the scene, but also the <code>x</code> and <code>y</code> axis.</li> + <li><strong>chaos</strong>: makes use of an edge detection kernel to create interesting visuals and also moves the textured image in a circular fashion for an interesting <em>chaotic</em> effect.</li> +</ul> + + +<p> + Below is a glimpse of what these effects are going to look like: +</p> + +<img src="/img/in-practice/breakout/postprocessing_effects.png" alt="Postprocessing effects in OpenGL Breakout game"/> + +<p> + Operating on a 2D quad, the vertex shader looks as follows: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec4 vertex; // &lt;vec2 position, vec2 texCoords&gt; + +out vec2 TexCoords; + +uniform bool chaos; +uniform bool confuse; +uniform bool shake; +uniform float time; + +void main() +{ + gl_Position = vec4(vertex.xy, 0.0f, 1.0f); + vec2 texture = vertex.zw; + if (chaos) + { + float strength = 0.3; + vec2 pos = vec2(texture.x + sin(time) * strength, texture.y + cos(time) * strength); + TexCoords = pos; + } + else if (confuse) + { + TexCoords = vec2(1.0 - texture.x, 1.0 - texture.y); + } + else + { + TexCoords = texture; + } + if (shake) + { + float strength = 0.01; + gl_Position.x += cos(time * 10) * strength; + gl_Position.y += cos(time * 15) * strength; + } +} +</code></pre> + +<p> + Based on whatever uniform is set to <code>true</code>, the vertex shader takes different paths. If either <var>chaos</var> or <var>confuse</var> is set to <code>true</code>, the vertex shader will manipulate the texture coordinates to move the scene around (either translate texture coordinates in a circle-like fashion, or inverse them). Because we set the texture wrapping methods to <code>GL_REPEAT</code>, the chaos effect will cause the scene to repeat itself at various parts of the quad. Additionally if <var>shake</var> is set to <code>true</code>, it will move the vertex positions around by a small amount, as if the screen shakes. Note that <var>chaos</var> and <var>confuse</var> shouldn't be <code>true</code> at the same time while <var>shake</var> is able to work with any of the other effects on. +</p> + +<p> + In addition to offsetting the vertex positions or texture coordinates, we'd also like to create some visual change as soon as any of the effects are active. We can accomplish this within the fragment shader: +</p> + +<pre><code> +#version 330 core +in vec2 TexCoords; +out vec4 color; + +uniform sampler2D scene; +uniform vec2 offsets[9]; +uniform int edge_kernel[9]; +uniform float blur_kernel[9]; + +uniform bool chaos; +uniform bool confuse; +uniform bool shake; + +void main() +{ + color = vec4(0.0f); + vec3 sample[9]; + // sample from texture offsets if using convolution matrix + if(chaos || shake) + for(int i = 0; i &lt; 9; i++) + sample[i] = vec3(texture(scene, TexCoords.st + offsets[i])); + + // process effects + if (chaos) + { + for(int i = 0; i &lt; 9; i++) + color += vec4(sample[i] * edge_kernel[i], 0.0f); + color.a = 1.0f; + } + else if (confuse) + { + color = vec4(1.0 - texture(scene, TexCoords).rgb, 1.0); + } + else if (shake) + { + for(int i = 0; i &lt; 9; i++) + color += vec4(sample[i] * blur_kernel[i], 0.0f); + color.a = 1.0f; + } + else + { + color = texture(scene, TexCoords); + } +} + +</code></pre> + +<p> + This long shader almost directly builds upon the fragment shader from the framebuffers chapter and processes several postprocessing effects based on the effect type activated. This time though, the offset matrix and convolution kernels are defined as a uniform that we set from the OpenGL code. The advantage is that we only have to set this once, instead of recalculating these matrices each fragment shader run. For example, the <var>offsets</var> matrix is configured as follows: +</p> + +<pre><code> +float offset = 1.0f / 300.0f; +float offsets[9][2] = { + { -offset, offset }, // top-left + { 0.0f, offset }, // top-center + { offset, offset }, // top-right + { -offset, 0.0f }, // center-left + { 0.0f, 0.0f }, // center-center + { offset, 0.0f }, // center - right + { -offset, -offset }, // bottom-left + { 0.0f, -offset }, // bottom-center + { offset, -offset } // bottom-right +}; +<function id='44'>glUniform</function>2fv(<function id='45'>glGetUniformLocation</function>(shader.ID, "offsets"), 9, (float*)offsets); +</code></pre> + +<p> + Since all of the concepts of managing (multisampled) framebuffers were already extensively discussed in earlier chapters, I won't delve into the details this time. Below you'll find the code of a <fun>PostProcessor</fun> class that manages initialization, writing/reading the framebuffers, and rendering a screen quad. You should be able to understand the code if you understood the framebuffers and anti-aliasing chapter: +</p> + +<ul> + <li><strong>PostProcessor</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/post_processor.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/post_processor.cpp" target="_blank">code</a>.</li> +</ul> + +<p> + What is interesting to note here are the <fun>BeginRender</fun> and <fun>EndRender</fun> functions. Since we have to render the entire game scene into the framebuffer we can conventiently call <fun>BeginRender()</fun> and <fun>EndRender()</fun> before and after the scene's rendering code respectively. The class will then handle the behind-the-scenes framebuffer operations. For example, using the <fun>PostProcessor</fun> class will look like this within the game's <fun>Render</fun> function: +</p> + +<pre><code> +PostProcessor *Effects; + +void Game::Render() +{ + if (this-&gt;State == GAME_ACTIVE) + { + Effects-&gt;BeginRender(); + // draw background + // draw level + // draw player + // draw particles + // draw ball + Effects-&gt;EndRender(); + Effects-&gt;Render(<function id='47'>glfwGetTime</function>()); + } +} +</code></pre> + +<p> + Wherever we want, we can now conveniently set the required effect property of the postprocessing class to <code>true</code> and its effect will be immediately active. +</p> + +<h3>Shake it</h3> +<p> + As a (practical) demonstration of these effects we'll emulate the visual impact of the ball when it hits a solid concrete block. By enabling the shake effect for a short period of time wherever a solid collision occurs, it'll look like the collision had a stronger impact. +</p> + +<p> + We want to enable the screen shake effect only over a small period of time. We can get this to work by creating a variable called <var>ShakeTime</var> that manages the duration the shake effect is supposed to be active. Wherever a solid collision occurs, we reset this variable to a specific duration: +</p> + +<pre><code> +float ShakeTime = 0.0f; + +void Game::DoCollisions() +{ + for (GameObject &box : this-&gt;Levels[this-&gt;Level].Bricks) + { + if (!box.Destroyed) + { + Collision collision = CheckCollision(*Ball, box); + if (std::get&lt;0&gt;(collision)) // if collision is true + { + // destroy block if not solid + if (!box.IsSolid) + box.Destroyed = true; + else + { // if block is solid, enable shake effect + ShakeTime = 0.05f; + Effects-&gt;Shake = true; + } + [...] + } + } + } + [...] +} +</code></pre> + +<p> + Then within the game's <fun>Update</fun> function, we decrease the <var>ShakeTime</var> variable until it's <code>0.0</code> after which we disable the shake effect: +</p> + +<pre><code> +void Game::Update(float dt) +{ + [...] + if (ShakeTime &gt; 0.0f) + { + ShakeTime -= dt; + if (ShakeTime &lt;= 0.0f) + Effects-&gt;Shake = false; + } +} +</code></pre> + +<p> + Then each time we hit a solid block, the screen briefly starts to shake and blur, giving the player some visual feedback the ball collided with a solid object. +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/postprocessing_shake.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/postprocessing_shake.png" class="clean"/> + </video> +</div> + +<p> + You can find the updated source code of the game class <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/7.game.cpp" target="_blank">here</a>. +</p> + +<p> + In the <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups" target="_blank">next</a> chapter about powerups we'll bring the other two postprocessing effects to good use. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Powerups.html b/pub/In-Practice/2D-Game/Powerups.html @@ -0,0 +1,734 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Powerups</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Powerups</h1> +<p> + Breakout is close to finished, but it would be cool to add at least one more gameplay mechanic so it's not your average standard Breakout clone; what about powerups? +</p> + +<p> + The idea is that whenever a brick is destroyed, the brick has a small chance of spawning a powerup block. Such a block will slowly fall downwards and if it collides with the player paddle, an interesting effect occurs based on the type of powerup. For example, one powerup makes the paddle larger, and another powerup allows the ball to pass through objects. We also include several negative powerups that affect the player in a negative way. +</p> + +<p> + We can model a powerup as a <fun>GameObject</fun> with a few extra properties. That's why we define a class <fun>PowerUp</fun> that inherits from <fun>GameObject</fun>: +</p> + +<pre><code> +const glm::vec2 SIZE(60.0f, 20.0f); +const glm::vec2 VELOCITY(0.0f, 150.0f); + +class PowerUp : public GameObject +{ +public: + // powerup state + std::string Type; + float Duration; + bool Activated; + // constructor + PowerUp(std::string type, glm::vec3 color, float duration, + glm::vec2 position, Texture2D texture) + : GameObject(position, SIZE, texture, color, VELOCITY), + Type(type), Duration(duration), Activated() + { } +}; +</code></pre> + +<p> + A <fun>PowerUp</fun> is just a <fun>GameObject</fun> with extra state, so we can simply define it in a single header file which you can find <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/power_up.h" target="_blank">here</a>. +</p> + +<p> + Each powerup defines its type as a string, a duration for how long it is active, and whether it is currently activated. Within Breakout we're going to feature a total of 4 positive powerups and 2 negative powerups: +</p> + +<img src="/img/in-practice/breakout/powerups.png" class="clean" alt="PowerUps used in OpenGL Breakoout"/> + +<ul> + <li><strong>Speed</strong>: increases the velocity of the ball by 20%. </li> + <li><strong>Sticky</strong>: when the ball collides with the paddle, the ball remains stuck to the paddle unless the spacebar is pressed again. This allows the player to better position the ball before releasing it. </li> + <li><strong>Pass-Through</strong>: collision resolution is disabled for non-solid blocks, allowing the ball to pass through multiple blocks.</li> + <li><strong>Pad-Size-Increase</strong>: increases the width of the paddle by 50 pixels.</li> + <li><strong>Confuse</strong>: activates the confuse postprocessing effect for a short period of time, confusing the user. </li> + <li><strong>Chaos</strong>: activates the chaos postprocessing effect for a short period of time, heavily disorienting the user.</li> +</ul> + +<p> + You can find the textures here: +</p> + +<ul> + <li><strong>Textures</strong>: <a href="/img/in-practice/breakout/textures/powerup_speed.png" target="_blank">Speed</a>, <a href="/img/in-practice/breakout/textures/powerup_sticky.png" target="_blank">Sticky</a>, <a href="/img/in-practice/breakout/textures/powerup_passthrough.png" target="_blank">Pass-Through</a>, <a href="/img/in-practice/breakout/textures/powerup_increase.png" target="_blank">Pad-Size-Increase</a>, <a href="/img/in-practice/breakout/textures/powerup_confuse.png" target="_blank">Confuse</a>, <a href="/img/in-practice/breakout/textures/powerup_chaos.png" target="_blank">Chaos</a>. +</ul> + +<p> + Similar to the level block textures, each of the powerup textures is completely grayscale. This makes sure the color of the powerups remain balanced whenever we multiply them with a color vector. +</p> + +<p> + Because powerups have state, a duration, and certain effects associated with them, we would like to keep track of all the powerups currently active in the game; we store them in a vector: +</p> + +<pre><code> +class Game { + public: + [...] + std::vector&lt;PowerUp&gt; PowerUps; + [...] + void SpawnPowerUps(GameObject &block); + void UpdatePowerUps(float dt); +}; +</code></pre> + +<p> + We've also defined two functions for managing powerups. <fun>SpawnPowerUps</fun> spawns a powerups at the location of a given block and <fun>UpdatePowerUps</fun> manages all powerups currently active within the game. +</p> + +<h3>Spawning PowerUps</h3> +<p> + Each time a block is destroyed we would like to, given a small chance, spawn a powerup. This functionality is found inside the game's <fun>SpawnPowerUps</fun> function: +</p> + +<pre><code> +bool ShouldSpawn(unsigned int chance) +{ + unsigned int random = rand() % chance; + return random == 0; +} +void Game::SpawnPowerUps(GameObject &block) +{ + if (ShouldSpawn(75)) // 1 in 75 chance + this-&gt;PowerUps.push_back( + PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, tex_speed + )); + if (ShouldSpawn(75)) + this-&gt;PowerUps.push_back( + PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, tex_sticky + ); + if (ShouldSpawn(75)) + this-&gt;PowerUps.push_back( + PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, tex_pass + )); + if (ShouldSpawn(75)) + this-&gt;PowerUps.push_back( + PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, tex_size + )); + if (ShouldSpawn(15)) // negative powerups should spawn more often + this-&gt;PowerUps.push_back( + PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, tex_confuse + )); + if (ShouldSpawn(15)) + this-&gt;PowerUps.push_back( + PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, tex_chaos + )); +} +</code></pre> + +<p> + The <fun>SpawnPowerUps</fun> function creates a new <fun>PowerUp</fun> object based on a given chance (1 in 75 for normal powerups and 1 in 15 for negative powerups) and sets their properties. Each powerup is given a specific color to make them more recognizable for the user and a duration in seconds based on its type; here a duration of <code>0.0f</code> means its duration is infinite. Additionally, each powerup is given the position of the destroyed block and one of the textures from the beginning of this chapter. +</p> + +<h3>Activating PowerUps</h3> +<p> + We then have to update the game's <fun>DoCollisions</fun> function to not only check for brick and paddle collisions, but also collisions between the paddle and each non-destroyed PowerUp. Note that we call <fun>SpawnPowerUps</fun> directly after a block is destroyed. +</p> + +<pre><code> +void Game::DoCollisions() +{ + for (GameObject &box : this-&gt;Levels[this-&gt;Level].Bricks) + { + if (!box.Destroyed) + { + Collision collision = CheckCollision(*Ball, box); + if (std::get&lt;0&gt;(collision)) // if collision is true + { + // destroy block if not solid + if (!box.IsSolid) + { + box.Destroyed = true; + this-&gt;SpawnPowerUps(box); + } + [...] + } + } + } + [...] + for (PowerUp &powerUp : this-&gt;PowerUps) + { + if (!powerUp.Destroyed) + { + if (powerUp.Position.y &gt;= this-&gt;Height) + powerUp.Destroyed = true; + if (CheckCollision(*Player, powerUp)) + { // collided with player, now activate powerup + ActivatePowerUp(powerUp); + powerUp.Destroyed = true; + powerUp.Activated = true; + } + } + } +} +</code></pre> + +<p> + For all powerups not yet destroyed, we check if the powerup either reached the bottom edge of the screen or collided with the paddle. In both cases the powerup is destroyed, but when collided with the paddle, it is also activated. +</p> + +<p> + Activating a powerup is accomplished by settings its <var>Activated</var> property to <code>true</code> and enabling the powerup's effect by giving it to the <fun>ActivatePowerUp</fun> function: +</p> + +<pre><code> +void ActivatePowerUp(PowerUp &powerUp) +{ + if (powerUp.Type == "speed") + { + Ball-&gt;Velocity *= 1.2; + } + else if (powerUp.Type == "sticky") + { + Ball-&gt;Sticky = true; + Player-&gt;Color = glm::vec3(1.0f, 0.5f, 1.0f); + } + else if (powerUp.Type == "pass-through") + { + Ball-&gt;PassThrough = true; + Ball-&gt;Color = glm::vec3(1.0f, 0.5f, 0.5f); + } + else if (powerUp.Type == "pad-size-increase") + { + Player-&gt;Size.x += 50; + } + else if (powerUp.Type == "confuse") + { + if (!Effects-&gt;Chaos) + Effects-&gt;Confuse = true; // only activate if chaos wasn't already active + } + else if (powerUp.Type == "chaos") + { + if (!Effects-&gt;Confuse) + Effects-&gt;Chaos = true; + } +} +</code></pre> + +<p> + The purpose of <fun>ActivatePowerUp</fun> is exactly as it sounds: it activates the effect of a powerup as we've described at the start of this chapter. We check the type of the powerup and change the game state accordingly. For the <code>"sticky"</code> and <code>"pass-through"</code> effect, we also change the color of the paddle and the ball respectively to give the user some feedback as to which effect is currently active. +</p> + +<p> + Because the sticky and pass-through effects somewhat change the game logic we store their effect as a property of the ball object; this way we can change the game logic based on whatever effect on the ball is currently active. The only thing we've changed in the <fun>BallObject</fun> header is the addition of these two properties, but for completeness' sake its updated code is listed below: +</p> + +<ul> + <li><strong>BallObject</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/ball_object.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/ball_object.cpp" target="_blank">code</a>.</li> +</ul> + +<p> + We can then easily implement the sticky effect by slightly updating the <fun>DoCollisions</fun> function at the collision code between the ball and the paddle: +</p> + +<pre><code> +if (!Ball-&gt;Stuck && std::get&lt;0&gt;(result)) +{ + [...] + Ball-&gt;Stuck = Ball-&gt;Sticky; +} +</code></pre> + +<p> + Here we set the ball's <var>Stuck</var> property equal to the ball's <var>Sticky</var> property. If the sticky effect is activated, the ball will end up stuck to the player paddle whenever it collides; the user then has to press the spacebar again to release the ball. +</p> + +<p> + A similar small change is made for the pass-through effect within the same <fun>DoCollisions</fun> function. When the ball's <var>PassThrough</var> property is set to <code>true</code> we do not perform any collision resolution on the non-solid bricks. +</p> + +<pre><code> +Direction dir = std::get&lt;1&gt;(collision); +glm::vec2 diff_vector = std::get&lt;2&gt;(collision); +if (!(Ball-&gt;PassThrough && !box.IsSolid)) +{ + if (dir == LEFT || dir == RIGHT) // horizontal collision + { + [...] + } + else + { + [...] + } +} +</code></pre> + +<p> + The other effects are activated by simply modifying the game's state like the ball's velocity, the paddle's size, or an effect of the <fun>PostProcesser</fun> object. +</p> + +<h3>Updating PowerUps</h3> +<p> + Now all that is left to do is make sure that powerups are able to move once they've spawned and that they're deactivated as soon as their duration runs out; otherwise powerups will stay active forever. +</p> + +<p> + Within the game's <fun>UpdatePowerUps</fun> function we move the powerups based on their velocity and decrease the active powerups their duration. Whenever a powerup's duration is decreased to <code>0.0f</code>, its effect is deactivated and the relevant variables are reset to their original state: +</p> + +<pre><code> +void Game::UpdatePowerUps(float dt) +{ + for (PowerUp &powerUp : this-&gt;PowerUps) + { + powerUp.Position += powerUp.Velocity * dt; + if (powerUp.Activated) + { + powerUp.Duration -= dt; + + if (powerUp.Duration &lt;= 0.0f) + { + // remove powerup from list (will later be removed) + powerUp.Activated = false; + // deactivate effects + if (powerUp.Type == "sticky") + { + if (!isOtherPowerUpActive(this-&gt;PowerUps, "sticky")) + { // only reset if no other PowerUp of type sticky is active + Ball-&gt;Sticky = false; + Player-&gt;Color = glm::vec3(1.0f); + } + } + else if (powerUp.Type == "pass-through") + { + if (!isOtherPowerUpActive(this-&gt;PowerUps, "pass-through")) + { // only reset if no other PowerUp of type pass-through is active + Ball-&gt;PassThrough = false; + Ball-&gt;Color = glm::vec3(1.0f); + } + } + else if (powerUp.Type == "confuse") + { + if (!isOtherPowerUpActive(this-&gt;PowerUps, "confuse")) + { // only reset if no other PowerUp of type confuse is active + Effects-&gt;Confuse = false; + } + } + else if (powerUp.Type == "chaos") + { + if (!isOtherPowerUpActive(this-&gt;PowerUps, "chaos")) + { // only reset if no other PowerUp of type chaos is active + Effects-&gt;Chaos = false; + } + } + } + } + } + this-&gt;PowerUps.erase(std::remove_if(this-&gt;PowerUps.begin(), this-&gt;PowerUps.end(), + [](const PowerUp &powerUp) { return powerUp.Destroyed && !powerUp.Activated; } + ), this-&gt;PowerUps.end()); +} +</code></pre> + +<p> + You can see that for each effect we disable it by resetting the relevant items to their original state. We also set the powerup's <var>Activated</var> property to <code>false</code>. At the end of <fun>UpdatePowerUps</fun> we then loop through the <var>PowerUps</var> vector and erase each powerup if they are destroyed <strong>and</strong> deactivated. We use the <fun>remove_if</fun> function from the <fun>algorithm</fun> header to erase these items given a lambda predicate. +</p> + +<note> + The <fun>remove_if</fun> function moves all elements for which the lambda predicate is true to the end of the container object and returns an iterator to the start of this <em>removed elements</em> range. The container's <fun>erase</fun> function then takes this iterator and the vector's end iterator to remove all the elements between these two iterators. +</note> + +<p> + It may happen that while one of the powerup effects is active, another powerup of the same type collides with the player paddle. In that case we have more than 1 powerup of that type currently active within the game's <var>PowerUps</var> vector. Whenever one of these powerups gets deactivated, we don't want to disable its effects yet since another powerup of the same type may still be active. For this reason we use the <fun>IsOtherPowerUpActive</fun> function to check if there is still another powerup active of the same type. Only if this function returns <code>false</code> we deactivate the powerup. This way, the powerup's duration of a given type is extended to the duration of its last activated powerup: +</p> + +<pre><code> +bool IsOtherPowerUpActive(std::vector&lt;PowerUp&gt; &powerUps, std::string type) +{ + for (const PowerUp &powerUp : powerUps) + { + if (powerUp.Activated) + if (powerUp.Type == type) + return true; + } + return false; +} +</code></pre> + +<p> + The function checks for all activated powerups if there is still a powerup active of the same type and if so, returns <code>true</code>. +</p> + +<p> + The last thing left to do is render the powerups: +</p> + +<pre><code> +void Game::Render() +{ + if (this->State == GAME_ACTIVE) + { + [...] + for (PowerUp &powerUp : this-&gt;PowerUps) + if (!powerUp.Destroyed) + powerUp.Draw(*Renderer); + [...] + } +} +</code></pre> + +<p> + Combine all this functionality and we have a working powerup system that not only makes the game more fun, but also a lot more challenging. It'll look a bit like this: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/in-practice/breakout/powerups.mp4" type="video/mp4" /> + <img src="/img/in-practice/breakout/powerups_video.png" class="clean"/> + </video> +</div> + +<p> + You can find the updated game code here (there we also reset all powerup effects whenever the level is reset): +</p> + +<ul> + <li><strong>Game</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/8.game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/8.game.cpp" target="_blank">code</a>.</li> +</ul> + + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Render-text.html b/pub/In-Practice/2D-Game/Render-text.html @@ -0,0 +1,748 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Render text</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Render-text</h1> +<p> + In this chapter we'll be adding the final enhancements to the game by adding a life system, a win condition, and feedback in the form of rendered text. This chapter heavily builds upon the earlier introduced <a href="https://learnopengl.com/In-Practice/Text-Rendering" target="_blank">Text Rendering</a> chapter so it is highly advised to first work your way through that chapter if you haven't already. +</p> + +<p> + In Breakout all text rendering code is encapsulated within a class called <fun>TextRenderer</fun> that features the initialization of the FreeType library, render configuration, and the actual render code. You can find the code of the <fun>TextRenderer</fun> class here: +</p> + +<ul> + <li><strong>TextRenderer</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/text_renderer.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/text_renderer.cpp" target="_blank">code</a>.</li> + <li><strong>Text shaders</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shaders/text.vs" target="_blank">vertex</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shaders/text.frag" target="_blank">fragment</a>.</li> +</ul> + +<p> + The content of the text renderer's functions is almost exactly the same as the code from the text rendering chapter. However, the code for rendering glyphs onto the screen is slightly different: +</p> + +<pre><code> +void TextRenderer::RenderText(std::string text, float x, float y, float scale, glm::vec3 color) +{ + [...] + for (c = text.begin(); c != text.end(); c++) + { + float xpos = x + ch.Bearing.x * scale; + float ypos = y + (this-&gt;Characters['H'].Bearing.y - ch.Bearing.y) * scale; + + float w = ch.Size.x * scale; + float h = ch.Size.y * scale; + // update VBO for each character + float vertices[6][4] = { + { xpos, ypos + h, 0.0f, 1.0f }, + { xpos + w, ypos, 1.0f, 0.0f }, + { xpos, ypos, 0.0f, 0.0f }, + + { xpos, ypos + h, 0.0f, 1.0f }, + { xpos + w, ypos + h, 1.0f, 1.0f }, + { xpos + w, ypos, 1.0f, 0.0f } + }; + [...] + } +} +</code></pre> + +<p> + The reason for it being slightly different is that we use a different orthographic projection matrix from the one we've used in the text rendering chapter. In the text rendering chapter all <code>y</code> values ranged from bottom to top, while in the Breakout game all <code>y</code> values range from top to bottom with a <code>y</code> coordinate of <code>0.0</code> corresponding to the top edge of the screen. This means we have to slightly modify how we calculate the vertical offset. +</p> + +<p> + Since we now render downwards from <fun>RenderText</fun>'s <var>y</var> parameter, we calculate the vertical offset as the distance a glyph is pushed downwards from the top of the glyph space. Looking back at the glyph metrics image from FreeType, this is indicated by the red arrow: +</p> + +<img src="/img/in-practice/breakout/glyph_offset.png" alt="Vertical offset of a FreeType glyph from the top of its glyph space for vertically inversed orthographic projection matrix in OpenGL"/> + +<p> + To calculate this vertical offset we need to get the top of the glyph space (the length of the black vertical arrow from the origin). Unfortunately, FreeType has no such metric for us. What we do know is that that some glyphs always touch this top edge; characters like 'H', 'T' or 'X'. So what if we calculate the length of this red vector by subtracting <code>bearingY</code> from any of these <em>top-reaching</em> glyphs by <code>bearingY</code> of the glyph in question. This way, we push the glyph down based on how far its top point differs from the top edge. +</p> + +<pre><code> +float ypos = y + (this-&gt;Characters['H'].Bearing.y - ch.Bearing.y) * scale; +</code></pre> + +<p> + In addition to updating the <code>ypos</code> calculation, we also switched the order of the vertices a bit to make sure all the vertices are still front facing when multiplied with the current orthographic projection matrix (as discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Face-culling" target="_blank">face culling</a> chapter). +</p> + +<p> + Adding the <fun>TextRenderer</fun> to the game is easy: +</p> + +<pre><code> +TextRenderer *Text; + +void Game::Init() +{ + [...] + Text = new TextRenderer(this-&gt;Width, this-&gt;Height); + Text->Load("fonts/ocraext.TTF", 24); +} +</code></pre> + +<p> + The text renderer is initialized with a font called OCR A Extended that you can download from <a href="http://fontzone.net/font-details/ocr-a-extended" target="_blank">here</a>. If the font is not to your liking, feel free to use a different font. +</p> + +<p> + Now that we have a text renderer, let's finish the gameplay mechanics. +</p> + +<h2>Player lives</h2> +<p> + Instead of immediately resetting the game as soon as the ball reaches the bottom edge, we'd like to give the player a few extra chances. We do this in the form of player lives, where the player begins with an initial number of lives (say <code>3</code>) and each time the ball touches the bottom edge, the player's life total is decreased by 1. Only when the player's life total becomes <code>0</code> we reset the game. This makes it easier for the player to finish a level while also building tension. +</p> + +<p> + We keep count of the lives of a player by adding it to the game class (initialized within the constructor to a value of <code>3</code>): +</p> + +<pre><code> +class Game +{ + [...] + public: + unsigned int Lives; +} +</code></pre> + +<p> + We then modify the game's <fun>Update</fun> function to, instead of resetting the game, decrease the player's life total, and only reset the game once the life total reaches <code>0</code>: +</p> + +<pre><code> +void Game::Update(float dt) +{ + [...] + if (Ball-&gt;Position.y &gt;= this-&gt;Height) // did ball reach bottom edge? + { + --this-&gt;Lives; + // did the player lose all his lives? : Game over + if (this-&gt;Lives == 0) + { + this-&gt;ResetLevel(); + this-&gt;State = GAME_MENU; + } + this-&gt;ResetPlayer(); + } +} +</code></pre> + +<p> + As soon as the player is game over (<var>lives</var> equals <code>0</code>), we reset the level and change the game state to <var>GAME_MENU</var> which we'll get to later. +</p> + +<p> + Don't forget to reset the player's life total as soon as we reset the game/level: +</p> + +<pre><code> +void Game::ResetLevel() +{ + [...] + this-&gt;Lives = 3; +} +</code></pre> + +<p> + The player now has a working life total, but has no way of seeing how many lives he currently has while playing the game. That's where the text renderer comes in: +</p> + +<pre><code> +void Game::Render() +{ + if (this-&gt;State == GAME_ACTIVE) + { + [...] + std::stringstream ss; ss &lt;&lt; this-&gt;Lives; + Text-&gt;RenderText("Lives:" + ss.str(), 5.0f, 5.0f, 1.0f); + } +} +</code></pre> + +<p> + Here we convert the number of lives to a string, and display it at the top-left of the screen. It'll now look a bit like this: +</p> + + <img src="/img/in-practice/breakout/render_text_lives.png" class="clean" alt="Rendered text with FreeType in OpenGL displaying the life total of the player"/> + +<p> + As soon as the ball touches the bottom edge, the player's life total is decreased which is instantly visible at the top-left of the screen. +</p> + +<h2>Level selection</h2> +<p> + Whenever the user is in the game state <var>GAME_MENU</var>, we'd like to give the player the control to select the level he'd like to play in. With either the 'w' or 's' key the player should be able to scroll through any of the levels we loaded. Whenever the player feels like the chosen level is indeed the level he'd like to play in, he can press the enter key to switch from the game's <var>GAME_MENU</var> state to the <var>GAME_ACTIVE</var> state. +</p> + +<p> + Allowing the player to choose a level is not too difficult. All we have to do is increase or decrease the game class's <var>Level</var> variable based on whether he pressed 'w' or 's' respectively: +</p> + +<pre><code> +if (this-&gt;State == GAME_MENU) +{ + if (this-&gt;Keys[GLFW_KEY_ENTER]) + this-&gt;State = GAME_ACTIVE; + if (this-&gt;Keys[GLFW_KEY_W]) + this-&gt;Level = (this-&gt;Level + 1) % 4; + if (this-&gt;Keys[GLFW_KEY_S]) + { + if (this-&gt;Level &gt; 0) + --this-&gt;Level; + else + this-&gt;Level = 3; + } +} +</code></pre> + +<p> + We use the modulus operator (<code>%</code>) to make sure the <var>Level</var> variable remains within the acceptable level range (between <code>0</code> and <code>3</code>). +</p> + +<p> + We also want to define what we want to render when we're in the menu state. We'd like to give the player some instructions in the form of text and also display the selected level in the background. +</p> + +<pre><code> +void Game::Render() +{ + if (this-&gt;State == GAME_ACTIVE || this-&gt;State == GAME_MENU) + { + [...] // Game state's rendering code + } + if (this-&gt;State == GAME_MENU) + { + Text-&gt;RenderText("Press ENTER to start", 250.0f, Height / 2, 1.0f); + Text-&gt;RenderText("Press W or S to select level", 245.0f, Height / 2 + 20.0f, 0.75f); + } +} +</code></pre> + +<p> + Here we render the game whenever we're in either the <var>GAME_ACTIVE</var> state or the <var>GAME_MENU</var> state, and whenever we're in the <var>GAME_MENU</var> state we also render two lines of text to inform the player to select a level and/or accept his choice. Note that for this to work when launching the game you do have to set the game's state as <var>GAME_MENU</var> by default. +</p> + +<img src="/img/in-practice/breakout/render_text_select.png" class="clean" alt="Selecting levels with FreeType rendered text in OpenGL"/> + +<p> + It looks great, but once you try to run the code you'll probably notice that as soon as you press either the 'w' or the 's' key, the game rapidly scrolls through the levels making it difficult to select the level you want to play in. This happens because the game records the key press over frames until we release the key. This causes the <fun>ProcessInput</fun> function to process the pressed key more than once. +</p> + +<p> + We can solve this issue with a little trick commonly found within GUI systems. The trick is to, not only record the keys currently pressed, but also store the keys that have been processed once, until released again. We then check (before processing) whether the key has not yet been processed, and if so, process this key after which we store this key as being processed. Once we want to process the same key again without the key having been released, we do not process the key. This probably sounds somewhat confusing, but as soon as you see it in practice it (probably) starts to make sense. +</p> + +<p> + First we have to create another array of bool values to indicate which keys have been processed. We define this within the game class: +</p> + +<pre><code> +class Game +{ + [...] + public: + bool KeysProcessed[1024]; +} +</code></pre> + +<p> + We then set the relevant key(s) to <code>true</code> as soon as they're processed and make sure to only process the key if it wasn't processed before (until released): +</p> + +<pre><code> +void Game::ProcessInput(float dt) +{ + if (this-&gt;State == GAME_MENU) + { + if (this-&gt;Keys[GLFW_KEY_ENTER] && !this-&gt;KeysProcessed[GLFW_KEY_ENTER]) + { + this-&gt;State = GAME_ACTIVE; + this-&gt;KeysProcessed[GLFW_KEY_ENTER] = true; + } + if (this-&gt;Keys[GLFW_KEY_W] && !this-&gt;KeysProcessed[GLFW_KEY_W]) + { + this-&gt;Level = (this-&gt;Level + 1) % 4; + this-&gt;KeysProcessed[GLFW_KEY_W] = true; + } + if (this-&gt;Keys[GLFW_KEY_S] && !this-&gt;KeysProcessed[GLFW_KEY_S]) + { + if (this-&gt;Level &gt; 0) + --this-&gt;Level; + else + this-&gt;Level = 3; + this-&gt;KeysProcessed[GLFW_KEY_S] = true; + } + } + [...] +} +</code></pre> + +<p> + Now as soon as the key's value in the <var>KeysProcessed</var> array has not yet been set, we process the key and set its value to <code>true</code>. Next time we reach the <code>if</code> condition of the same key, it will have been processed so we'll pretend we never pressed the button until it's released again. +</p> + +<p> + Within GLFW's key callback function we then need to reset the key's processed value as soon as it's released so we can process it again the next time it's pressed: +</p> + +<pre><code> +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) +{ + [...] + if (key &gt;= 0 && key &lt; 1024) + { + if (action == GLFW_PRESS) + Breakout.Keys[key] = true; + else if (action == GLFW_RELEASE) + { + Breakout.Keys[key] = false; + Breakout.KeysProcessed[key] = false; + } + } +} +</code></pre> + +<p> + Launching the game gives us a neat level select screen that now precisely selects a single level per key press, no matter how long we press he key. +</p> + +<h2>Winning</h2> +<p> + Currently the player is able to select levels, play the game, and fail in doing so to lose. It is kind of unfortunate if the player finds out after destroying all the bricks he cannot in any way win the game. So let's fix that. +</p> + +<p> + The player wins when all of the non-solid blocks have been destroyed. We already created a function to check for this condition in the <fun>GameLevel</fun> class: +</p> + +<pre><code> +bool GameLevel::IsCompleted() +{ + for (GameObject &tile : this-&gt;Bricks) + if (!tile.IsSolid && !tile.Destroyed) + return false; + return true; +} +</code></pre> + +<p> + We check all bricks in the game level and if a single non-solid brick isn't yet destroyed we return <code>false</code>. All we have to do is check for this condition in the game's <fun>Update</fun> function and as soon as it returns <code>true</code> we change the game state to <var>GAME_WIN</var>: +</p> + +<pre><code> +void Game::Update(float dt) +{ + [...] + if (this-&gt;State == GAME_ACTIVE && this-&gt;Levels[this-&gt;Level].IsCompleted()) + { + this-&gt;ResetLevel(); + this-&gt;ResetPlayer(); + Effects-&gt;Chaos = true; + this-&gt;State = GAME_WIN; + } +} +</code></pre> + +<p> + Whenever the level is completed while the game is active, we reset the game and display a small victory message in the <var>GAME_WIN</var> state. For fun we'll also enable the chaos effect while in the <var>GAME_WIN</var> screen. In the <fun>Render</fun> function we'll congratulate the player and ask him to either restart or quit the game: +</p> + +<pre><code> +void Game::Render() +{ + [...] + if (this-&gt;State == GAME_WIN) + { + Text-&gt;RenderText( + "You WON!!!", 320.0, Height / 2 - 20.0, 1.0, glm::vec3(0.0, 1.0, 0.0) + ); + Text-&gt;RenderText( + "Press ENTER to retry or ESC to quit", 130.0, Height / 2, 1.0, glm::vec3(1.0, 1.0, 0.0) + ); + } +} +</code></pre> + +<p> + Then we of course have to actually catch the mentioned keys: +</p> + +<pre><code> +void Game::ProcessInput(float dt) +{ + [...] + if (this-&gt;State == GAME_WIN) + { + if (this-&gt;Keys[GLFW_KEY_ENTER]) + { + this-&gt;KeysProcessed[GLFW_KEY_ENTER] = true; + Effects-&gt;Chaos = false; + this-&gt;State = GAME_MENU; + } + } +} +</code></pre> + +<p> + If you're then good enough to actually win the game, you'd get the following image: +</p> + + <img src="/img/in-practice/breakout/render_text_win.png" class="clean" alt="Image of winning in OpenGL Breakout with FreeType rendered text"/> + +<p> + And that is it! The final piece of the puzzle of the Breakout game we've been actively working on. Try it out, customize it to your liking, and show it to all your family and friends! +</p> + +<p> + You can find the final version of the game's code below: +</p> + +<ul> + <li><strong>Game</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/game.cpp" target="_blank">code</a>.</li> +</ul> + + <h2>Further reading</h2> +<ul> + <li><a href="https://www.websiteplanet.com/blog/best-free-fonts/" target="_blank">70+ Best Free Fonts for Designers</a>: summarized list of a large group of fonts to use in your project for personal or commercial use.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Rendering-Sprites.html b/pub/In-Practice/2D-Game/Rendering-Sprites.html @@ -0,0 +1,604 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Rendering Sprites</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Rendering-Sprites</h1> +<p> + To bring some life to the currently black abyss of our game world, we will render sprites to fill the void. A <def>sprite</def> has many definitions, but it's effectively not much more than a 2D image used together with some data to position it in a larger world (e.g. position, rotation, and size). Basically, sprites are the render-able image/texture objects we use in a 2D game. +</p> + +<p> + We can, just like we did in previous chapters, create a 2D shape out of vertex data, pass all data to the GPU, and transform it all by hand. However, in a larger application like this we rather have some abstractions on rendering 2D shapes. If we were to manually define these shapes and transformations for each object, it'll quickly get messy. +</p> + +<p> + In this chapter we'll define a rendering class that allows us to render a large amount of unique sprites with a minimal amount of code. This way, we're abstracting the gameplay code from the gritty OpenGL rendering code as is commonly done in larger projects. First, we have to set up a proper projection matrix though. +</p> + +<h2>2D projection matrix</h2> +<p> + We know from the <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">coordinate systems</a> chapter that a projection matrix converts all view-space coordinates to clip-space (and then to normalized device) coordinates. By generating the appropriate projection matrix we can work with different coordinates that are easier to work with, compared to directly specifying all coordinates as normalized device coordinates. +</p> + +<p> + We don't need any perspective applied to the coordinates, since the game is entirely in 2D, so an orthographic projection matrix would suit the rendering quite well. Because an orthographic projection matrix directly transforms all coordinates to normalized device coordinates, we can choose to specify the world coordinates as screen coordinates by defining the projection matrix as follows: +</p> + +<pre><code> +glm::mat4 projection = <function id='59'>glm::ortho</function>(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f); +</code></pre> + +<p> + The first four arguments specify in order the left, right, bottom, and top part of the projection frustum. This projection matrix transforms all <code>x</code> coordinates between <code>0</code> and <code>800</code> to <code>-1</code> and <code>1</code>, and all <code>y</code> coordinates between <code>0</code> and <code>600</code> to <code>-1</code> and <code>1</code>. Here we specified that the top of the frustum has a <code>y</code> coordinate of <code>0</code>, while the bottom has a <code>y</code> coordinate of <code>600</code>. The result is that the top-left coordinate of the scene will be at (<code>0,0</code>) and the bottom-right part of the screen is at coordinate (<code>800,600</code>), just like screen coordinates; the world-space coordinates directly correspond to the resulting pixel coordinates. +</p> + +<img src="/img/in-practice/breakout/projection.png" class="clean" alt="Orthographic projection in OpenGL"/> + +<p> + This allows us to specify all vertex coordinates equal to the pixel coordinates they end up in on the screen, which is rather intuitive for 2D games. +</p> + +<h2>Rendering sprites</h2> +<p> + Rendering an actual sprite shouldn't be too complicated. We create a textured quad that we can transform with a model matrix, after which we project it using the previously defined orthographic projection matrix. +</p> + +<note> + Since Breakout is a single-scene game, there is no need for a view/camera matrix. Using the projection matrix we can directly transform the world-space coordinates to normalized device coordinates. +</note> + +<p> + To transform a sprite, we use the following vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec4 vertex; // &lt;vec2 position, vec2 texCoords&gt; + +out vec2 TexCoords; + +uniform mat4 model; +uniform mat4 projection; + +void main() +{ + TexCoords = vertex.zw; + gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0); +} +</code></pre> + +<p> + Note that we store both the position and texture-coordinate data in a single <fun>vec4</fun> variable. Because both the position and texture coordinates contain two floats, we can combine them in a single vertex attribute. +</p> + +<p> + The fragment shader is relatively straightforward as well. We take a texture and a color vector that both affect the final color of the fragment. By having a uniform color vector, we can easily change the color of sprites from the game-code: +</p> + +<pre><code> +#version 330 core +in vec2 TexCoords; +out vec4 color; + +uniform sampler2D image; +uniform vec3 spriteColor; + +void main() +{ + color = vec4(spriteColor, 1.0) * texture(image, TexCoords); +} +</code></pre> + +<p> + To make the rendering of sprites more organized, we define a <fun>SpriteRenderer</fun> class that is able to render a sprite with just a single function. Its definition is as follows: +</p> + +<pre><code> +class SpriteRenderer +{ + public: + SpriteRenderer(Shader &shader); + ~SpriteRenderer(); + + void DrawSprite(Texture2D &texture, glm::vec2 position, + glm::vec2 size = glm::vec2(10.0f, 10.0f), float rotate = 0.0f, + glm::vec3 color = glm::vec3(1.0f)); + private: + Shader shader; + unsigned int quadVAO; + + void initRenderData(); +}; +</code></pre> + +<p> + The <def>SpriteRenderer</def> class hosts a shader object, a single vertex array object, and a render and initialization function. Its constructor takes a shader object that it uses for all future rendering. +</p> + +<h3>Initialization</h3> +<p> + First, let's delve into the <fun>initRenderData</fun> function that configures the <var>quadVAO</var>: +</p> + +<pre><code> +void SpriteRenderer::initRenderData() +{ + // configure VAO/VBO + unsigned int VBO; + float vertices[] = { + // pos // tex + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 0.0f + }; + + <function id='33'>glGenVertexArrays</function>(1, &this-&gt;quadVAO); + <function id='12'>glGenBuffers</function>(1, &VBO); + + <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); + <function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + <function id='27'>glBindVertexArray</function>(this-&gt;quadVAO); + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); + <function id='30'>glVertexAttribPointer</function>(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0); + <function id='27'>glBindVertexArray</function>(0); +} +</code></pre> + +<p> + Here we first define a set of vertices with (<code>0,0</code>) being the top-left corner of the quad. This means that when we apply translation or scaling transformations on the quad, they're transformed from the top-left position of the quad. This is commonly accepted in 2D graphics and/or GUI systems where elements' positions correspond to the top-left corner of the elements. +</p> + +<p> + Next we simply sent the vertices to the GPU and configure the vertex attributes, which in this case is a single vertex attribute. We only have to define a single VAO for the sprite renderer since all sprites share the same vertex data. +</p> + +<h3>Rendering</h3> +<p> + Rendering sprites is not too difficult; we use the sprite renderer's shader, configure a model matrix, and set the relevant uniforms. What is important here is the order of transformations: +</p> + +<pre><code> +void SpriteRenderer::DrawSprite(Texture2D &texture, glm::vec2 position, + glm::vec2 size, float rotate, glm::vec3 color) +{ + // prepare transformations + this-&gt;shader.Use(); + glm::mat4 model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, glm::vec3(position, 0.0f)); + + model = <function id='55'>glm::translate</function>(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.0f)); + model = <function id='57'>glm::rotate</function>(model, <function id='63'>glm::radians</function>(rotate), glm::vec3(0.0f, 0.0f, 1.0f)); + model = <function id='55'>glm::translate</function>(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, 0.0f)); + + model = <function id='56'>glm::scale</function>(model, glm::vec3(size, 1.0f)); + + this-&gt;shader.SetMatrix4("model", model); + this-&gt;shader.SetVector3f("spriteColor", color); + + <function id='49'>glActiveTexture</function>(GL_TEXTURE0); + texture.Bind(); + + <function id='27'>glBindVertexArray</function>(this-&gt;quadVAO); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); + <function id='27'>glBindVertexArray</function>(0); +} +</code></pre> + +<p> + When trying to position objects somewhere in a scene with rotation and scaling transformations, it is advised to first scale, then rotate, and finally translate the object. Because multiplying matrices occurs from right to left, we transform the matrix in reverse order: translate, rotate, and then scale. +</p> + +<p> + The rotation transformation may still seem a bit daunting. We know from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter that rotations always revolve around the origin (<code>0,0</code>). Because we specified the quad's vertices with (<code>0,0</code>) as the top-left coordinate, all rotations will rotate around this point of (<code>0,0</code>). The <def>origin of rotation</def> is at the top-left of the quad, which produces undesirable results. What we want to do is move the origin of rotation to the center of the quad so the quad neatly rotates around this origin, instead of rotating around the top-left of the quad. We solve this by translating the quad by half its size first, so its center is at coordinate (<code>0,0</code>) before rotating. +</p> + +<img src="/img/in-practice/breakout/rotation-origin.png" class="clean" alt="Properly rotating at the center of origin of the quad"/> + +<p> + Since we first scale the quad, we have to take the size of the sprite into account when translating to the sprite's center, which is why we multiply with the sprite's <var>size</var> vector. Once the rotation transformation is applied, we reverse the previous translation. +</p> + +<p> + Combining all these transformations, we can position, scale, and rotate each sprite in any way we like. Below you can find the complete source code of the sprite renderer: +</p> + +<ul> + <li><strong>SpriteRenderer</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/sprite_renderer.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/sprite_renderer.cpp" target="_blank">code</a> </li> +</ul> + +<h2>Hello sprite</h2> +<p> + With the <fun>SpriteRenderer</fun> class we finally have the ability to render actual images to the screen! Let's initialize one within the game code and load our favorite <a href="/img/textures/awesomeface.png" target="_blank">texture</a> while we're at it: +</p> + +<pre><code> +SpriteRenderer *Renderer; + +void Game::Init() +{ + // load shaders + ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite"); + // configure shaders + glm::mat4 projection = <function id='59'>glm::ortho</function>(0.0f, static_cast&lt;float&gt;(this-&gt;Width), + static_cast&lt;float&gt;(this-&gt;Height), 0.0f, -1.0f, 1.0f); + ResourceManager::GetShader("sprite").Use().SetInteger("image", 0); + ResourceManager::GetShader("sprite").SetMatrix4("projection", projection); + // set render-specific controls + Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite")); + // load textures + ResourceManager::LoadTexture("textures/awesomeface.png", true, "face"); +} +</code></pre> + +<p> + Then within the render function we can render our beloved mascot to see if everything works as it should: +</p> + +<pre><code> +void Game::Render() +{ + Renderer-&gt;DrawSprite(ResourceManager::GetTexture("face"), + glm::vec2(200.0f, 200.0f), glm::vec2(300.0f, 400.0f), 45.0f, glm::vec3(0.0f, 1.0f, 0.0f)); +} +</code></pre> + +<p> + Here we position the sprite somewhat close to the center of the screen with its height being slightly larger than its width. We also rotate it by 45 degrees and give it a green color. Note that the position we give the sprite equals the top-left vertex of the sprite's quad. +</p> + +<p> + If you did everything right you should get the following output: +</p> + +<img src="/img/in-practice/breakout/rendering-sprites.png" class="clean" alt="Image of a rendered sprite using our custom-made OpenGL's SpriteRenderer class"/> + +<p> + You can find the updated game class's source code <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/3.game.cpp" target="_blank">here</a>. +</p> + +<p> + Now that we got the render systems working, we can put it to good use in the <a href="https://learnopengl.com/In-Practice/2D-Game/Levels" target="_blank">next</a> chapter where we'll work on building the game's levels. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/2D-Game/Setting-up.html b/pub/In-Practice/2D-Game/Setting-up.html @@ -0,0 +1,485 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Setting up</h1> +<h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Setting-up</h1> +<p> + Before we get started with the game mechanics, we first need to set up a simple framework for the game to reside in. The game will use several third party libraries of which most have been introduced in earlier chapters. Wherever a new library is required, it will be properly introduced. +</p> + +<p> + First, we define a so called <def>uber</def> game class that contains all relevant render and gameplay code. The idea of such a game class is that it (sort of) organizes your game code, while also decoupling all windowing code from the game. This way, you could use the same class in a completely different windowing library (like SDL or SFML for example) without much effort. +</p> + +<note> + There are thousands of ways of trying to abstract and generalize game/graphics code into classes and objects. What you will see in these chapters is just one (relatively simple) approach to solve this issue. If you feel there is a better approach, try to come up with your own improvement of the implementation. +</note> + +<p> + The game class hosts an initialization function, an update function, a function to process input, and a render function: +</p> + +<pre><code> +class Game +{ + public: + // game state + GameState State; + bool Keys[1024]; + unsigned int Width, Height; + // constructor/destructor + Game(unsigned int width, unsigned int height); + ~Game(); + // initialize game state (load all shaders/textures/levels) + void Init(); + // game loop + void ProcessInput(float dt); + void Update(float dt); + void Render(); +}; +</code></pre> + +<p> + The class hosts what you may expect from a game class. We initialize the game with a width and height (the resolution you want to play the game in) and use the <fun>Init</fun> function to load shaders, textures, and initialize all gameplay state. We can process input as stored within the <var>Keys</var> array by calling <fun>ProcessInput</fun>, and update all gameplay events (like player/ball movement) in the <fun>Update</fun> function. Last, we can render the game by calling <fun>Render</fun>. Note that we split the movement logic from the render logic. +</p> + +<p> + The <fun>Game</fun> class also hosts a variable called <var>State</var> which is of type <def>GameState</def> as defined below: +</p> + +<pre><code> +// Represents the current state of the game +enum GameState { + GAME_ACTIVE, + GAME_MENU, + GAME_WIN +}; +</code></pre> + +<p> + This allows us to keep track of what state the game is currently in. This way, we can decide to adjust rendering and/or processing based on the current state of the game (we probably render and process different items when we're in the game's menu for example). +</p> + +<p> + As of now, the functions of the game class are completely empty since we have yet to write the actual game code, but here are the Game class's <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/2.game.h" target="_blank">header</a> + and <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/2.game.cpp" target="_blank">code</a> file. +</p> + +<h2>Utility</h2> +<p> + Since we're creating a large application we'll frequently have to re-use several OpenGL concepts, like textures and shaders. It thus makes sense to create a more easy-to-use interface for these two items as similarly done in one of the earlier chapters where we created a shader class. +</p> + +<p> + We define a shader class that generates a compiled shader (or generates error messages if it fails) from two or three strings (if a geometry shader is present). The shader class also contains a lot of useful utility functions to quickly set uniform values. We also define a texture class that generates a 2D texture image (based on its properties) from a byte array and a given width and height. Again, the texture class also hosts utility functions. +</p> + +<p> + We won't delve into the details of the classes since by now you should easily understand how they work. For this reason you can find the header and code files, fully commented, below: +</p> + +<ul> + <li><strong>Shader</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shader.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/shader.cpp" target="_blank">code</a>.</li> + <li><strong>Texture</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/texture.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/texture.cpp" target="_blank">code</a>.</li> +</ul> + +<p> + Note that the current texture class is solely designed for 2D textures only, but could easily be extended for alternative texture types. +</p> + +<h2>Resource management</h2> +<p> + While the shader and texture classes function great by themselves, they do require either a byte array or a list of strings for initialization. We could easily embed file loading code within the classes themselves, but this slightly violates the <def>single responsibility principle</def>. We'd prefer these classes to only focus on either textures or shaders respectively, and not necessarily their file-loading mechanics. +</p> + +<p> + For this reason it is often considered a more organized approach to create a single entity designed for loading game-related resources called a <def>resource manager</def>. There are several approaches to creating a resource manager; for this chapter we chose to use a singleton static resource manager that is (due to its static nature) always available throughout the project, hosting all loaded resources and their relevant loading functionality. +</p> + +<p> + Using a singleton class with static functionality has several advantages and disadvantages, with its disadvantages mostly being the loss of several OOP properties and less control over construction/destruction. However, for relatively small projects like this it is easy to work with. +</p> + +<p> + Like the other class files, the resource manager is listed below: +</p> + +<ul> + <li><strong>Resource Manager</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/resource_manager.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/resource_manager.cpp" target="_blank">code</a>.</li> +</ul> + +<p> + Using the resource manager, we can easily load shaders into the program like: +</p> + +<pre><code> +Shader shader = ResourceManager::LoadShader("vertex.vs", "fragment.vs", nullptr, "test"); +// then use it +shader.Use(); +// or +ResourceManager::GetShader("test").Use(); +</code></pre> + +<p> + The defined <fun>Game</fun> class, together with the resource manager and the easily manageable <fun>Shader</fun> and <fun>Texture2D</fun> classes, form the basis for the next chapters as we'll be extensively using these classes to implement the Breakout game. +</p> + +<h2>Program</h2> +<p> + We still need a window for the game and set some initial OpenGL state as we make use of OpenGL's <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">blending</a> functionality. We do not enable depth testing, since the game is entirely in 2D. All vertices are defined with the same z-values so enabling depth testing would be of no use and likely cause z-fighting. +</p> + +<p> + The startup code of the Breakout game is relatively simple: we create a window with GLFW, register a few callback functions, create the <fun>Game</fun> object, and propagate all relevant functionality to the game class. The code is given below: +</p> + +<ul> + <li><strong>Program</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/2.program.cpp" target="_blank">code</a>.</li> +</ul> + +<p> + Running the code should give you the following output: +</p> + +<img src="/img/in-practice/breakout/setting-up.png" class="clean" alt="Blank image of start of Breakout game in OpenGL"/> + +<p> + By now we have a solid framework for the upcoming chapters; we'll be continuously extending the game class to host new functionality. Hop over to the <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites" target="_blank">next</a> chapter once you're ready. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/Debugging.html b/pub/In-Practice/Debugging.html @@ -0,0 +1,888 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Debugging</h1> +<h1 id="content-url" style='display:none;'>In-Practice/Debugging</h1> +<p> + Graphics programming can be a lot of fun, but it can also be a large source of frustration whenever something isn't rendering just right, or perhaps not even rendering at all! Seeing as most of what we do involves manipulating pixels, it can be difficult to figure out the cause of error whenever something doesn't work the way it's supposed to. Debugging these kinds of <em>visual</em> errors is different than what you're used to when debugging errors on the CPU. We have no console to output text to, no breakpoints to set on GLSL code, and no way of easily checking the state of GPU execution. +</p> + +<p> + In this chapter we'll look into several techniques and tricks of debugging your OpenGL program. Debugging in OpenGL is not too difficult to do and getting a grasp of its techniques definitely pays out in the long run. +</p> + +<h2>glGetError()</h2> +<p> + The moment you incorrectly use OpenGL (like configuring a buffer without first binding any) it will take notice and generate one or more user error flags behind the scenes. We can query these error flags using a function named <fun>glGetError</fun> that checks the error flag(s) set and returns an error value if OpenGL got misused: +</p> + +<pre><code> +GLenum glGetError(); +</code></pre> + +<p> + + The moment <fun>glGetError</fun> is called, it returns either an error flag or no error at all. The error codes that <fun>glGetError</fun> can return are listed below: +</p> + +<table> + <tr> + <th>Flag</th> + <th>Code</th> + <th>Description</th> + </tr> + <tr> + <td><var>GL_NO_ERROR</var></td> + <td>0</td> + <td>No user error reported since the last call to <fun>glGetError</fun>.</td> + </tr> + <tr> + <td><var>GL_INVALID_ENUM</var></td> + <td>1280</td> + <td>Set when an enumeration parameter is not legal.</td> + </tr> + <tr> + <td><var>GL_INVALID_VALUE</var></td> + <td>1281</td> + <td>Set when a value parameter is not legal.</td> + </tr> + <tr> + <td><var>GL_INVALID_OPERATION</var></td> + <td>1282</td> + <td>Set when the state for a command is not legal for its given parameters.</td> + </tr> + <tr> + <td><var>GL_STACK_OVERFLOW</var></td> + <td>1283</td> + <td>Set when a stack pushing operation causes a stack overflow.</td> + </tr> + <tr> + <td><var>GL_STACK_UNDERFLOW</var></td> + <td>1284</td> + <td>Set when a stack popping operation occurs while the stack is at its lowest point.</td> + </tr> + <tr> + <td><var>GL_OUT_OF_MEMORY</var></td> + <td>1285</td> + <td>Set when a memory allocation operation cannot allocate (enough) memory.</td> + </tr> + <tr> + <td><var>GL_INVALID_FRAMEBUFFER_OPERATION</var></td> + <td>1286</td> + <td>Set when reading or writing to a framebuffer that is not complete.</td> + </tr> +</table> + +<p> + Within OpenGL's function documentation you can always find the error codes a function generates the moment it is incorrectly used. For instance, if you take a look at the documentation of <a href="http://docs.gl/gl3/glBindTextur%65" target="_blank"><function id='48'>glBindTexture</function></a> function, you can find all the user error codes it could generate under the <em>Errors</em> section. +</p> + +<p> + The moment an error flag is set, no other error flags will be reported. Furthermore, the moment <fun>glGetError</fun> is called it clears all error flags (or only one if on a distributed system, see note below). This means that if you call <fun>glGetError</fun> once at the end of each frame and it returns an error, you can't conclude this was the only error, and the source of the error could've been anywhere in the frame. +</p> + +<note> + Note that when OpenGL runs distributedly like frequently found on X11 systems, other user error codes can still be generated as long as they have different error codes. Calling <fun>glGetError</fun> then only resets one of the error code flags instead of all of them. Because of this, it is recommended to call <fun>glGetError</fun> inside a loop. +</note> + +<pre><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, tex); +std::cout &lt;&lt; glGetError() &lt;&lt; std::endl; // returns 0 (no error) + +<function id='52'>glTexImage2D</function>(GL_TEXTURE_3D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, data); +std::cout &lt;&lt; glGetError() &lt;&lt; std::endl; // returns 1280 (invalid enum) + +<function id='50'>glGenTextures</function>(-5, textures); +std::cout &lt;&lt; glGetError() &lt;&lt; std::endl; // returns 1281 (invalid value) + +std::cout &lt;&lt; glGetError() &lt;&lt; std::endl; // returns 0 (no error) +</code></pre> + +<p> + The great thing about <fun>glGetError</fun> is that it makes it relatively easy to pinpoint where any error may be and to validate the proper use of OpenGL. Let's say you get a black screen and you have no idea what's causing it: is the framebuffer not properly set? Did I forget to bind a texture? By calling <fun>glGetError</fun> all over your codebase, you can quickly catch the first place an OpenGL error starts showing up. +</p> + +<p> + By default <fun>glGetError</fun> only prints error numbers, which isn't easy to understand unless you've memorized the error codes. It often makes sense to write a small helper function to easily print out the error strings together with where the error check function was called: +</p> + +<pre><code> +GLenum glCheckError_(const char *file, int line) +{ + GLenum errorCode; + while ((errorCode = glGetError()) != GL_NO_ERROR) + { + std::string error; + switch (errorCode) + { + case GL_INVALID_ENUM: error = "INVALID_ENUM"; break; + case GL_INVALID_VALUE: error = "INVALID_VALUE"; break; + case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break; + case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break; + case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break; + case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break; + case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break; + } + std::cout &lt;&lt; error &lt;&lt; " | " &lt;&lt; file &lt;&lt; " (" &lt;&lt; line &lt;&lt; ")" &lt;&lt; std::endl; + } + return errorCode; +} +#define glCheckError() glCheckError_(__FILE__, __LINE__) +</code></pre> + +<p> + In case you're unaware of what the preprocessor directives <code>__FILE__</code> and <code>__LINE__</code> are: these variables get replaced during compile time with the respective file and line they were compiled in. If we decide to stick a large number of these <fun>glCheckError</fun> calls in our codebase it's helpful to more precisely know which <fun>glCheckError</fun> call returned the error. +</p> + +<pre><code> +<function id='32'>glBindBuffer</function>(GL_VERTEX_ARRAY, vbo); +glCheckError(); +</code></pre> + +<p> + This will give us the following output: +</p> + +<img src="/img/in-practice/debugging_glgeterror.png" alt="Output of glGetError in OpenGL debugging."/> + +<p> + <fun>glGetError</fun> doesn't help you too much as the information it returns is rather simple, but it does often help you catch typos or quickly pinpoint where in your code things went wrong; a simple but effective tool in your debugging toolkit. +</p> + + +<h2>Debug output</h2> +<p> + A less common, but more useful tool than <fun>glCheckError</fun> is an OpenGL extension called <def>debug output</def> that became part of core OpenGL since version 4.3. With the debug output extension, OpenGL itself will directly send an error or warning message to the user with a lot more details compared to <fun>glCheckError</fun>. Not only does it provide more information, it can also help you catch errors exactly where they occur by intelligently using a debugger. +</p> + +<note> + Debug output is core since OpenGL version 4.3, which means you'll find this functionality on any machine that runs OpenGL 4.3 or higher. If they're not available, its functionality can be queried from the <code>ARB_debug_output</code> or <code>AMD_debug_output</code> extension. Note that OS X does not seem to support debug output functionality (as gathered online). +</note> + +<p> + In order to start using debug output we have to request a debug output context from OpenGL at our initialization process. This process varies based on whatever windowing system you use; here we will discuss setting it up on GLFW, but you can find info on other systems in the additional resources at the end of the chapter. +</p> + +<h3>Debug output in GLFW</h3> +<p> + Requesting a debug context in GLFW is surprisingly easy as all we have to do is pass a hint to GLFW that we'd like to have a debug output context. We have to do this before we call <fun><function id='20'>glfwCreateWindow</function></fun>: +</p> + +<pre><code> +<function id='18'>glfwWindowHint</function>(GLFW_OPENGL_DEBUG_CONTEXT, true); +</code></pre> + +<p> + Once we've then initialized GLFW, we should have a debug context if we're using OpenGL version 4.3 or higher. If not, we have to take our chances and hope the system is still able to request a debug context. Otherwise we have to request debug output using its OpenGL extension(s). +</p> + +<note> + Using OpenGL in debug context can be significantly slower compared to a non-debug context, so when working on optimizations or releasing your application you want to remove GLFW's debug request hint. +</note> + +<p> + To check if we successfully initialized a debug context we can query OpenGL: +</p> + +<pre><code> +int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); +if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) +{ + // initialize debug output +} +</code></pre> + +<p> + The way debug output works is that we pass OpenGL an error logging function callback (similar to GLFW's input callbacks) and in the callback function we are free to process the OpenGL error data as we see fit; in our case we'll be displaying useful error data to the console. Below is the callback function prototype that OpenGL expects for debug output: +</p> + +<pre><code> +void APIENTRY glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, + GLsizei length, const char *message, const void *userParam); +</code></pre> + +<p> + Given the large set of data we have at our exposal, we can create a useful error printing tool like below: +</p> + +<pre><code> +void APIENTRY glDebugOutput(GLenum source, + GLenum type, + unsigned int id, + GLenum severity, + GLsizei length, + const char *message, + const void *userParam) +{ + // ignore non-significant error/warning codes + if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return; + + std::cout &lt;&lt; "---------------" &lt;&lt; std::endl; + std::cout &lt;&lt; "Debug message (" &lt;&lt; id &lt;&lt; "): " &lt;&lt; message &lt;&lt; std::endl; + + switch (source) + { + case GL_DEBUG_SOURCE_API: std::cout &lt;&lt; "Source: API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout &lt;&lt; "Source: Window System"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout &lt;&lt; "Source: Shader Compiler"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout &lt;&lt; "Source: Third Party"; break; + case GL_DEBUG_SOURCE_APPLICATION: std::cout &lt;&lt; "Source: Application"; break; + case GL_DEBUG_SOURCE_OTHER: std::cout &lt;&lt; "Source: Other"; break; + } std::cout &lt;&lt; std::endl; + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: std::cout &lt;&lt; "Type: Error"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout &lt;&lt; "Type: Deprecated Behaviour"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout &lt;&lt; "Type: Undefined Behaviour"; break; + case GL_DEBUG_TYPE_PORTABILITY: std::cout &lt;&lt; "Type: Portability"; break; + case GL_DEBUG_TYPE_PERFORMANCE: std::cout &lt;&lt; "Type: Performance"; break; + case GL_DEBUG_TYPE_MARKER: std::cout &lt;&lt; "Type: Marker"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: std::cout &lt;&lt; "Type: Push Group"; break; + case GL_DEBUG_TYPE_POP_GROUP: std::cout &lt;&lt; "Type: Pop Group"; break; + case GL_DEBUG_TYPE_OTHER: std::cout &lt;&lt; "Type: Other"; break; + } std::cout &lt;&lt; std::endl; + + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: std::cout &lt;&lt; "Severity: high"; break; + case GL_DEBUG_SEVERITY_MEDIUM: std::cout &lt;&lt; "Severity: medium"; break; + case GL_DEBUG_SEVERITY_LOW: std::cout &lt;&lt; "Severity: low"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout &lt;&lt; "Severity: notification"; break; + } std::cout &lt;&lt; std::endl; + std::cout &lt;&lt; std::endl; +} +</code></pre> + +<p> + Whenever debug output detects an OpenGL error, it will call this callback function and we'll be able to print out a large deal of information regarding the OpenGL error. Note that we ignore a few error codes that tend to not really display anything useful (like <code>131185</code> in NVidia drivers that tells us a buffer was successfully created). +</p> + +<p> + Now that we have the callback function it's time to initialize debug output: +</p> + +<pre><code> +if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) +{ + <function id='60'>glEnable</function>(GL_DEBUG_OUTPUT); + <function id='60'>glEnable</function>(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(glDebugOutput, nullptr); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); +} +</code></pre> + +<p> + Here we tell OpenGL to enable debug output. The <code><function id='60'>glEnable</function>(GL_DEBUG_SYNCRHONOUS)</code> call tells OpenGL to directly call the callback function the moment an error occurred. +</p> + +<h3>Filter debug output</h3> +<p> + With <fun>glDebugMessageControl</fun> you can potentially filter the type(s) of errors you'd like to receive a message from. In our case we decided to not filter on any of the sources, types, or severity rates. If we wanted to only show messages from the OpenGL API, that are errors, and have a high severity, we'd configure it as follows: +</p> + +<pre><code> +glDebugMessageControl(GL_DEBUG_SOURCE_API, + GL_DEBUG_TYPE_ERROR, + GL_DEBUG_SEVERITY_HIGH, + 0, nullptr, GL_TRUE); +</code></pre> + +<p> + Given our configuration, and assuming you have a context that supports debug output, every incorrect OpenGL command will now print a large bundle of useful data: +</p> + +<img src="/img/in-practice/debugging_debug_output.png" alt="Output of OpenGL debug output on a text console."/> + +<h3>Backtracking the debug error source</h3> +<p> + Another great trick with debug output is that you can relatively easy figure out the exact line or call an error occurred. By setting a breakpoint in <fun>DebugOutput</fun> at a specific error type (or at the top of the function if you don't care), the debugger will catch the error thrown and you can move up the call stack to whatever function caused the message dispatch: +</p> + + <img src="/img/in-practice/debugging_debug_output_breakpoint.png" alt="Setting a breaking and using the callstack in OpenGL to catch the line of an error with debug output."/> + +<p> + It requires some manual intervention, but if you roughly know what you're looking for it's incredibly useful to quickly determine which call causes an error. +</p> + +<h3>Custom error output</h3> +<p> + Aside from reading messages, we can also push messages to the debug output system with <fun>glDebugMessageInsert</fun>: +</p> + +<pre><code> +glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, + GL_DEBUG_SEVERITY_MEDIUM, -1, "error message here"); +</code></pre> + +<p> + This is especially useful if you're hooking into other application or OpenGL code that makes use of a debug output context. Other developers can quickly figure out any <em>reported</em> bug that occurs in your custom OpenGL code. +</p> + +<p> + In summary, debug output (if you can use it) is incredibly useful for quickly catching errors and is well worth the effort in setting up as it saves considerable development time. You can find a source code example <a href="/code_viewer_gh.php?code=src/7.in_practice/1.debugging/debugging.cpp" target="_blank">here</a> with both <fun>glGetError</fun> and debug output context configured; see if you can fix all the errors. +</p> + +<h2>Debugging shader output</h2> +<p> + When it comes to GLSL, we unfortunately don't have access to a function like <fun>glGetError</fun> nor the ability to step through the shader code. When you end up with a black screen or the completely wrong visuals, it's often difficult to figure out if something's wrong with the shader code. Yes, we have the compilation error reports that report syntax errors, but catching the semantic errors is another beast. +</p> + +<p> + One frequently used trick to figure out what is wrong with a shader is to evaluate all the relevant variables in a shader program by sending them directly to the fragment shader's output channel. By outputting shader variables directly to the output color channels, we can convey interesting information by inspecting the visual results. For instance, let's say we want to check if a model has correct normal vectors. We can pass them (either transformed or untransformed) from the vertex shader to the fragment shader where we'd then output the normals as follows: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec3 Normal; +[...] + +void main() +{ + [...] + FragColor.rgb = Normal; + FragColor.a = 1.0f; +} +</code></pre> + +<p> + By outputting a (non-color) variable to the output color channel like this we can quickly inspect if the variable is, as far as you can tell, displaying correct values. If, for instance, the visual result is completely black it is clear the normal vectors aren't correctly passed to the shaders; and when they are displayed it's relatively easy to check if they're (sort of) correct or not: +</p> + +<img src="/img/in-practice/debugging_glsl_output.png" alt="The image of a 3D model with its normal vectors displayed as the fragment shader output in OpenGL for debugging"/> + +<p> + From the visual results we can see the world-space normal vectors appear to be correct as the right sides of the backpack model is mostly colored red (which would mean the normals roughly point (correctly) towards the positive x axis). Similarly, the front side of the backpack is mostly colored towards the positive z axis (blue). +</p> + +<p> + This approach can easily extend to any type of variable you'd like to test. Whenever you get stuck and suspect there's something wrong with your shaders, try displaying multiple variables and/or intermediate results to see at which part of the algorithm something's missing or seemingly incorrect. +</p> + +<h2>OpenGL GLSL reference compiler</h2> +<p> + Each driver has its own quirks and tidbits; for instance, NVIDIA drivers are more flexible and tend to overlook some restrictions on the specification, while ATI/AMD drivers tend to better enforce the OpenGL specification (which is the better approach in my opinion). The result of this is that shaders on one machine may not work on the other due to driver differences. +</p> + +<p> + With years of experience you'll eventually get to learn the minor differences between GPU vendors, but if you want to be sure your shader code runs on all kinds of machines you can directly check your shader code against the official specification using OpenGL's GLSL <a href="https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/" target="_blank">reference compiler</a>. You can download the so called <def>GLSL lang validator</def> binaries from <a href="https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/" target="_blank">here</a> or its complete source code from <a href="https://github.com/KhronosGroup/glslang" target="_blank">here</a>. +</p> + +<p> + Given the binary GLSL lang validator you can easily check your shader code by passing it as the binary's first argument. Keep in mind that the GLSL lang validator determines the type of shader by a list of fixed extensions: +<p> + +<ul> + <li><code>.vert</code>: vertex shader.</li> + <li><code>.frag</code>: fragment shader.</li> + <li><code>.geom</code>: geometry shader.</li> + <li><code>.tesc</code>: tessellation control shader.</li> + <li><code>.tese</code>: tessellation evaluation shader.</li> + <li><code>.comp</code>: compute shader.</li> +</ul> + +<p> + Running the GLSL reference compiler is as simple as: +</p> + +<pre><code> +glsllangvalidator shaderFile.vert +</code></pre> + +<p> + Note that if it detects no error, it returns no output. Testing the GLSL reference compiler on a broken vertex shader gives the following output: +</p> + + <img src="/img/in-practice/debugging_glsl_reference_compiler.png" alt="Output of the GLSL reference compiler (GLSL lang validator) in OpenGL"/> + +<p> + It won't show you the subtle differences between AMD, NVidia, or Intel GLSL compilers, nor will it help you completely bug proof your shaders, but it does at least help you to check your shaders against the direct GLSL specification. +</p> + +<h2>Framebuffer output</h2> +<p> + Another useful trick for your debugging toolkit is displaying a framebuffer's content(s) in some pre-defined region of your screen. You're likely to use <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a> quite often and, as most of their magic happens behind the scenes, it's sometimes difficult to figure out what's going on. Displaying the content(s) of a framebuffer on your screen is a useful trick to quickly see if things look correct. +</p> + +<note> + Note that displaying the contents (attachments) of a framebuffer as explained here only works on texture attachments, not render buffer objects. +</note> + +<p> + Using a simple shader that only displays a texture, we can easily write a small helper function to quickly display any texture at the top-right of the screen: +</p> + +<pre><code> +// vertex shader +#version 330 core +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 texCoords; + +out vec2 TexCoords; + +void main() +{ + gl_Position = vec4(position, 0.0f, 1.0f); + TexCoords = texCoords; +} + +// fragment shader +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; + +uniform sampler2D fboAttachment; + +void main() +{ + FragColor = texture(fboAttachment, TexCoords); +} +</code></pre> + +<pre><code> +void DisplayFramebufferTexture(unsigned int textureID) +{ + if (!notInitialized) + { + // initialize shader and vao w/ NDC vertex coordinates at top-right of the screen + [...] + } + + <function id='49'>glActiveTexture</function>(GL_TEXTURE0); + <function id='28'>glUseProgram</function>(shaderDisplayFBOOutput); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, textureID); + <function id='27'>glBindVertexArray</function>(vaoDebugTexturedRect); + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); + <function id='27'>glBindVertexArray</function>(0); + <function id='28'>glUseProgram</function>(0); +} + +int main() +{ + [...] + while (!<function id='14'>glfwWindowShouldClose</function>(window)) + { + [...] + DisplayFramebufferTexture(fboAttachment0); + + <function id='24'>glfwSwapBuffers</function>(window); + } +} +</code></pre> + +<p> + This will give you a nice little window at the corner of your screen for debugging framebuffer output. Useful, for example, for determining if the normal vectors of the geometry pass in a deferred renderer look correct: +</p> + + <img src="/img/in-practice/debugging_fbo_output.png" alt="Framebuffer attachment output to a texture for debugging purposes in OpenGL"/> + +<p> + You can of course extend such a utility function to support rendering more than one texture. This is a quick and dirty way to get continuous feedback from whatever is in your framebuffer(s). +</p> + +<h2>External debugging software</h2> +<p> + When all else fails there is still the option to use a 3rd party tool to help us in our debugging efforts. Third party applications often inject themselves in the OpenGL drivers and are able to intercept all kinds of OpenGL calls to give you a large array of interesting data. These tools can help you in all kinds of ways like: profiling OpenGL function usage, finding bottlenecks, inspecting buffer memory, and displaying textures and framebuffer attachments. When you're working on (large) production code, these kinds of tools can become invaluable in your development process. +</p> + +<p> + I've listed some of the more popular debugging tools here; try out several of them to see which fits your needs the best. +</p> + +<!--<h3>gDebugger</h3> +<p> + gDebugger is a cross-platform and easy to use debugging tool for OpenGL applications. gDebugger sits alongside your running OpenGL application and provides a detailed overview of the running OpenGL state. You can pause the application at any moment to inspect the current state, texture memory and/or buffer usage. You can download gDebugger <a href="http://www.gremedy.com/" target="_blank">here</a>. +</p> + +<p> + Running gDebugger is as easy as opening the application, creating a new project and giving it the location and working directory of your OpenGL executable. +</p> + + <img src="/img/in-practice/debugging_external_gdebugger.png" alt="Image of gDebugger running on an OpenGL application."> +--> + +<!--<h3>APItrace</h3> +<p> + <a href="https://github.com/apitrace/apitrace" target="_blank">APItrace</a> +</p>--> + +<h3>RenderDoc</h3> +<p> + RenderDoc is a great (completely <a href="https://github.com/baldurk/renderdoc" target="_blank">open source</a>) standalone debugging tool. To start a capture, you specify the executable you'd like to capture and a working directory. The application then runs as usual, and whenever you want to inspect a particular frame, you let RenderDoc capture one or more frames at the executable's current state. Within the captured frame(s) you can view the pipeline state, all OpenGL commands, buffer storage, and textures in use. +</p> + +<img src="/img/in-practice/debugging_external_renderdoc.png" alt="Image of RenderDoc running on an OpenGL application."/> + +<h3>CodeXL</h3> +<p> + <a href="https://gpuopen.com/compute-product/codexl/" target="_blank">CodeXL</a> is GPU debugging tool released as both a standalone tool and a Visual Studio plugin. CodeXL gives a good set of information and is great for profiling graphics applications. CodeXL also works on NVidia or Intel cards, but without support for OpenCL debugging. +</p> + + <img src="/img/in-practice/debugging_external_codexl.png" alt="Image of CodeXL running on an OpenGL application."/> + +<p> + I personally don't have much experience with CodeXL since I found RenderDoc easier to use, but I've included it anyways as it looks to be a pretty solid tool and developed by one of the larger GPU manufacturers. +</p> + +<h3>NVIDIA Nsight</h3> +<p> + NVIDIA's popular <a href="https://developer.nvidia.com/nvidia-nsight-visual-studio-edition" target="_blank">Nsight</a> GPU debugging tool is not a standalone tool, but a plugin to either the Visual Studio IDE or the Eclipse IDE (NVIDIA now has a <a href="https://developer.nvidia.com/nsight-graphics" target="_blank">standalone version</a> as well). The Nsight plugin is an incredibly useful tool for graphics developers as it gives a large host of run-time statistics regarding GPU usage and the frame-by-frame GPU state. + </p> + +<p> + The moment you start your application from within Visual Studio (or Eclipse), using Nsight's debugging or profiling commands, Nsight will run within the application itself. The great thing about Nsight is that it renders an overlay GUI system from within your application that you can use to gather all kinds of interesting information about your application, both at run-time and during frame-by-frame analysis. +</p> + + <img src="/img/in-practice/debugging_external_nsight.png" alt="Image of RenderDoc running on an OpenGL application."/> + +<p> + Nsight is an incredibly useful tool, but it does come with one major drawback in that it only works on NVIDIA cards. If you are working on NVIDIA cards (and use Visual Studio) it's definitely worth a shot. +</p> + +<p> + I'm sure there's plenty of other debugging tools I've missed (some that come to mind are Valve's <a href="https://github.com/ValveSoftware/vogl" target="_blank">VOGL</a> and <a href="https://apitrace.github.io/" target="_blank">APItrace</a>), but I feel this list should already get you plenty of tools to experiment with. +</p> + +<h2>Additional resources</h2> +<ul> + <li><a href="http://retokoradi.com/2014/04/21/opengl-why-is-your-code-producing-a-black-window/" target="_blank">Why is your code producing a black window</a>: list of general causes by Reto Koradi of why your screen may not be producing any output.</li> + <li><a href="https://vallentin.dev/2015/02/23/debugging-opengl" target="_blank">Debug Output in OpenGL</a>: an extensive debug output write-up by Vallentin with detailed information on setting up a debug context on multiple windowing systems.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/In-Practice/Text-Rendering.html b/pub/In-Practice/Text-Rendering.html @@ -0,0 +1,758 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Text Rendering</h1> +<h1 id="content-url" style='display:none;'>In-Practice/Text-Rendering</h1> +<p> + At some stage of your graphics adventures you will want to draw text in OpenGL. Contrary to what you may expect, getting a simple string to render on screen is all but easy with a low-level API like OpenGL. If you don't care about rendering more than 128 different same-sized characters, then it's probably not too difficult. Things are getting difficult as soon as each character has a different width, height, and margin. Based on where you live, you may also need more than 128 characters, and what if you want to express special symbols for like mathematical expressions or sheet music symbols, and what about rendering text from top to bottom? Once you think about all these complicated matters of text, it wouldn't surprise you that this probably doesn't belong in a low-level API like OpenGL. +</p> + +<p> + Since there is no support for text capabilities within OpenGL, it is up to us to define a system for rendering text to the screen. There are no graphical primitives for text characters, we have to get creative. Some example techniques are: drawing letter shapes via <var>GL_LINES</var>, create 3D meshes of letters, or render character textures to 2D quads in a 3D environment. +</p> + +<p> + Most developers choose to render character textures onto quads. Rendering textured quads by itself shouldn't be too difficult, but getting the relevant character(s) onto a texture could prove challenging. In this chapter we'll explore several methods and implement a more advanced, but flexible technique for rendering text using the FreeType library. +</p> + +<h2>Classical text rendering: bitmap fonts</h2> +<p> + In the early days, rendering text involved selecting a font (or create one yourself) you'd like for your application and extracting all relevant characters out of this font to place them within a single large texture. Such a texture, that we call a <def>bitmap font</def>, contains all character symbols we want to use in predefined regions of the texture. These character symbols of the font are known as <def>glyphs</def>. Each glyph has a specific region of texture coordinates associated with them. Whenever you want to render a character, you select the corresponding glyph by rendering this section of the bitmap font to a 2D quad. +</p> + +<img src="/img/in-practice/bitmapfont.png" alt="Sprite sheet of characters"/> + +<p> + Here you can see how we would render the text 'OpenGL' by taking a bitmap font and sampling the corresponding glyphs from the texture (carefully choosing the texture coordinates) that we render on top of several quads. By enabling <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">blending</a> and keeping the background transparent, we will end up with just a string of characters rendered to the screen. This particular bitmap font was generated using Codehead's Bitmap <a href="http://www.codehead.co.uk/cbfg/" target="_blank">Font Generator</a>. +</p> + +<p> + This approach has several advantages and disadvantages. It is relatively easy to implement and because bitmap fonts are pre-rasterized, they're quite efficient. However, it is not particularly flexible. When you want to use a different font, you need to recompile a complete new bitmap font and the system is limited to a single resolution; zooming will quickly show pixelated edges. Furthermore, it is limited to a small character set, so Extended or Unicode characters are often out of the question. +</p> + +<p> + This approach was quite popular back in the day (and still is) since it is fast and works on any platform, but as of today more flexible approaches exist. One of these approaches is loading TrueType fonts using the FreeType library. +</p> + +<h2>Modern text rendering: FreeType</h2> +<p> + FreeType is a software development library that is able to load fonts, render them to bitmaps, and provide support for several font-related operations. It is a popular library used by Mac OS X, Java, PlayStation, Linux, and Android to name a few. What makes FreeType particularly attractive is that it is able to load TrueType fonts. +</p> + +<p> + A TrueType font is a collection of character glyphs not defined by pixels or any other non-scalable solution, but by mathematical equations (combinations of splines). Similar to vector images, the rasterized font images can be procedurally generated based on the preferred font height you'd like to obtain them in. By using TrueType fonts you can easily render character glyphs of various sizes without any loss of quality. +</p> + +<p> + FreeType can be downloaded from their <a href="http://www.freetype.org/" target="_blank">website</a>. You can choose to compile the library yourself or use one of their precompiled libraries if your target platform is listed. Be sure to link to <code>freetype.lib</code> and make sure your compiler knows where to find the header files. + </p> + +<p> + Then include the appropriate headers: +</p> + +<pre><code> +#include &lt;ft2build.h&gt; +#include FT_FREETYPE_H +</code></pre> + +<warning> + Due to how FreeType is developed (at least at the time of this writing), you cannot put their header files in a new directory; they should be located at the root of your include directories. Including FreeType like <code>#include &lt;FreeType/ft2build.h&gt;</code> will likely cause several header conflicts. +</warning> + +<p> + FreeType loads these TrueType fonts and, for each glyph, generates a bitmap image and calculates several metrics. We can extract these bitmap images for generating textures and position each character glyph appropriately using the loaded metrics. +</p> + +<p> + To load a font, all we have to do is initialize the FreeType library and load the font as a <def>face</def> as FreeType likes to call it. Here we load the <code>arial.ttf</code> TrueType font file that was copied from the <code>Windows/Fonts</code> directory: +</p> + +<pre><code> +FT_Library ft; +if (FT_Init_FreeType(&ft)) +{ + std::cout &lt;&lt; "ERROR::FREETYPE: Could not init FreeType Library" &lt;&lt; std::endl; + return -1; +} + +FT_Face face; +if (FT_New_Face(ft, "fonts/arial.ttf", 0, &face)) +{ + std::cout &lt;&lt; "ERROR::FREETYPE: Failed to load font" &lt;&lt; std::endl; + return -1; +} +</code></pre> + +<p> + Each of these FreeType functions returns a non-zero integer whenever an error occurred. + </p> + + <p> + Once we've loaded the face, we should define the pixel font size we'd like to extract from this face: +</p> + +<pre class="cpp"><code> +FT_Set_Pixel_Sizes(face, 0, 48); +</code></pre> + +<p> + The function sets the font's width and height parameters. Setting the width to <code>0</code> lets the face dynamically calculate the width based on the given height. +</p> + +<p> + A FreeType face hosts a collection of glyphs. We can set one of those glyphs as the active glyph by calling <fun>FT_Load_Char</fun>. Here we choose to load the character glyph 'X': +</p> + +<pre><code> +if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) +{ + std::cout &lt;&lt; "ERROR::FREETYTPE: Failed to load Glyph" &lt;&lt; std::endl; + return -1; +} +</code></pre> + +<p> + By setting <var>FT_LOAD_RENDER</var> as one of the loading flags, we tell FreeType to create an 8-bit grayscale bitmap image for us that we can access via <code>face-&gt;glyph-&gt;bitmap</code>. +</p> + +<p> + Each of the glyphs we load with FreeType however, do not have the same size (as we had with bitmap fonts). The bitmap image generated by FreeType is just large enough to contain the visible part of a character. For example, the bitmap image of the dot character '.' is much smaller in dimensions than the bitmap image of the character 'X'. For this reason, FreeType also loads several metrics that specify how large each character should be and how to properly position them. Next is an image from FreeType that shows all of the metrics it calculates for each character glyph: +</p> + + <img src="/img/in-practice/glyph.png" alt="Image of metrics of a Glyph as loaded by FreeType"/> + +<p> + Each of the glyphs reside on a horizontal <def>baseline</def> (as depicted by the horizontal arrow) where some glyphs sit exactly on top of this baseline (like 'X') or some slightly below the baseline (like 'g' or 'p'). These metrics define the exact offsets to properly position each glyph on the baseline, how large each glyph should be, and how many pixels we need to advance to render the next glyph. Next is a small list of the properties we'll be needing: +</p> + +<ul> + <li><strong>width</strong>: the width (in pixels) of the bitmap accessed via <code>face-&gt;glyph-&gt;bitmap.width</code>.</li> + <li><strong>height</strong>: the height (in pixels) of the bitmap accessed via <code>face-&gt;glyph-&gt;bitmap.rows</code>.</li> + <li><strong>bearingX</strong>: the horizontal bearing e.g. the horizontal position (in pixels) of the bitmap relative to the origin accessed via <code>face-&gt;glyph-&gt;bitmap_left</code>.</li> + <li><strong>bearingY</strong>: the vertical bearing e.g. the vertical position (in pixels) of the bitmap relative to the baseline accessed via <code>face-&gt;glyph-&gt;bitmap_top</code>.</li> + <li><strong>advance</strong>: the horizontal advance e.g. the horizontal distance (in 1/64th pixels) from the origin to the origin of the next glyph. Accessed via <code>face-&gt;glyph-&gt;advance.x</code>.</li> +</ul> + +<p> + We could load a character glyph, retrieve its metrics, and generate a texture each time we want to render a character to the screen, but it would be inefficient to do this each frame. We'd rather store the generated data somewhere in the application and query it whenever we want to render a character. We'll define a convenient <code>struct</code> that we'll store in a <fun>map</fun>: +</p> + +<pre><code> +struct Character { + unsigned int TextureID; // ID handle of the glyph texture + glm::ivec2 Size; // Size of glyph + glm::ivec2 Bearing; // Offset from baseline to left/top of glyph + unsigned int Advance; // Offset to advance to next glyph +}; + +std::map&lt;char, Character&gt; Characters; +</code></pre> + +<p> + For this chapter we'll keep things simple by restricting ourselves to the first 128 characters of the ASCII character set. For each character, we generate a texture and store its relevant data into a <fun>Character</fun> struct that we add to the <var>Characters</var> map. This way, all data required to render each character is stored for later use. +</p> + +<pre><code> +glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction + +for (unsigned char c = 0; c &lt; 128; c++) +{ + // load character glyph + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) + { + std::cout &lt;&lt; "ERROR::FREETYTPE: Failed to load Glyph" &lt;&lt; std::endl; + continue; + } + // generate texture + unsigned int texture; + <function id='50'>glGenTextures</function>(1, &texture); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, texture); + <function id='52'>glTexImage2D</function>( + GL_TEXTURE_2D, + 0, + GL_RED, + face-&gt;glyph-&gt;bitmap.width, + face-&gt;glyph-&gt;bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + face-&gt;glyph-&gt;bitmap.buffer + ); + // set texture options + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // now store character for later use + Character character = { + texture, + glm::ivec2(face-&gt;glyph-&gt;bitmap.width, face-&gt;glyph-&gt;bitmap.rows), + glm::ivec2(face-&gt;glyph-&gt;bitmap_left, face-&gt;glyph-&gt;bitmap_top), + face-&gt;glyph-&gt;advance.x + }; + Characters.insert(std::pair&lt;char, Character&gt;(c, character)); +} +</code></pre> + +<p> + Within the for loop we list over all the 128 characters of the ASCII set and retrieve their corresponding character glyphs. For each character: we generate a texture, set its options, and store its metrics. What is interesting to note here is that we use <var>GL_RED</var> as the texture's <code>internalFormat</code> and <code>format</code> arguments. The bitmap generated from the glyph is a grayscale 8-bit image where each color is represented by a single byte. For this reason we'd like to store each byte of the bitmap buffer as the texture's single color value. We accomplish this by creating a texture where each byte corresponds to the texture color's red component (first byte of its color vector). If we use a single byte to represent the colors of a texture we do need to take care of a restriction of OpenGL: +</p> + +<pre class="cpp"><code> +glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +</code></pre> + +<p> + OpenGL requires that textures all have a 4-byte alignment e.g. their size is always a multiple of 4 bytes. Normally this won't be a problem since most textures have a width that is a multiple of 4 and/or use 4 bytes per pixel, but since we now only use a single byte per pixel, the texture can have any possible width. By setting its unpack alignment to <code>1</code> we ensure there are no alignment issues (which could cause segmentation faults). +</p> + +<p> + Be sure to clear FreeType's resources once you're finished processing the glyphs: +</p> + +<pre><code> +FT_Done_Face(face); +FT_Done_FreeType(ft); +</code></pre> + +<h3>Shaders</h3> +<p> + To render the glyphs we'll be using the following vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec4 vertex; // &lt;vec2 pos, vec2 tex&gt; +out vec2 TexCoords; + +uniform mat4 projection; + +void main() +{ + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + TexCoords = vertex.zw; +} +</code></pre> + +<p> + We combine both the position and texture coordinate data into one <fun>vec4</fun>. The vertex shader multiplies the coordinates with a projection matrix and forwards the texture coordinates to the fragment shader: +</p> + +<pre><code> +#version 330 core +in vec2 TexCoords; +out vec4 color; + +uniform sampler2D text; +uniform vec3 textColor; + +void main() +{ + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); + color = vec4(textColor, 1.0) * sampled; +} +</code></pre> + +<p> + The fragment shader takes two uniforms: one is the mono-colored bitmap image of the glyph, and the other is a color uniform for adjusting the text's final color. We first sample the color value of the bitmap texture. Because the texture's data is stored in just its red component, we sample the <code>r</code> component of the texture as the sampled alpha value. By varying the output color's alpha value, the resulting pixel will be transparent for all the glyph's background colors and non-transparent for the actual character pixels. We also multiply the RGB colors by the <var>textColor</var> uniform to vary the text color. +</p> + +<p> + We do need to enable <a href="https://learnopengl.com/Advanced-OpenGL/Blending" target="_blank">blending</a> for this to work though: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_BLEND); +<function id='70'>glBlendFunc</function>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +</code></pre> + +<p> + For the projection matrix we'll be using an orthographic projection matrix. For rendering text we (usually) do not need perspective, and using an orthographic projection matrix also allows us to specify all vertex coordinates in screen coordinates if we set it up as follows: +</p> + +<pre><code> +glm::mat4 projection = <function id='59'>glm::ortho</function>(0.0f, 800.0f, 0.0f, 600.0f); +</code></pre> + +<p> + We set the projection matrix's bottom parameter to <code>0.0f</code> and its top parameter equal to the window's height. The result is that we specify coordinates with <code>y</code> values ranging from the bottom part of the screen (<code>0.0f</code>) to the top part of the screen (<code>600.0f</code>). This means that the point (<code>0.0</code>, <code>0.0</code>) now corresponds to the bottom-left corner. +</p> + +<p> + Last up is creating a VBO and VAO for rendering the quads. For now we reserve enough memory when initiating the VBO so that we can later update the VBO's memory when rendering characters: +</p> + +<pre><code> +unsigned int VAO, VBO; +<function id='33'>glGenVertexArrays</function>(1, &VAO); +<function id='12'>glGenBuffers</function>(1, &VBO); +<function id='27'>glBindVertexArray</function>(VAO); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +<function id='30'>glVertexAttribPointer</function>(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0); +<function id='27'>glBindVertexArray</function>(0); +</code></pre> + +<p> + The 2D quad requires <code>6</code> vertices of <code>4</code> floats each, so we reserve <code>6 * 4</code> floats of memory. Because we'll be updating the content of the VBO's memory quite often we'll allocate the memory with <var>GL_DYNAMIC_DRAW</var>. +</p> + +<h3>Render line of text</h3> +<p> + To render a character, we extract the corresponding <fun>Character</fun> struct of the <var>Characters</var> map and calculate the quad's dimensions using the character's metrics. With the quad's calculated dimensions we dynamically generate a set of 6 vertices that we use to update the content of the memory managed by the VBO using <fun><function id='90'>glBufferSubData</function></fun>. +</p> + +<p> + We create a function called <fun>RenderText</fun> that renders a string of characters: +</p> + +<pre><code> +void RenderText(Shader &s, std::string text, float x, float y, float scale, glm::vec3 color) +{ + // activate corresponding render state + s.Use(); + <function id='44'>glUniform</function>3f(<function id='45'>glGetUniformLocation</function>(s.Program, "textColor"), color.x, color.y, color.z); + <function id='49'>glActiveTexture</function>(GL_TEXTURE0); + <function id='27'>glBindVertexArray</function>(VAO); + + // iterate through all characters + std::string::const_iterator c; + for (c = text.begin(); c != text.end(); c++) + { + Character ch = Characters[*c]; + + float xpos = x + ch.Bearing.x * scale; + float ypos = y - (ch.Size.y - ch.Bearing.y) * scale; + + float w = ch.Size.x * scale; + float h = ch.Size.y * scale; + // update VBO for each character + float vertices[6][4] = { + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos, ypos, 0.0f, 1.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + + { xpos, ypos + h, 0.0f, 0.0f }, + { xpos + w, ypos, 1.0f, 1.0f }, + { xpos + w, ypos + h, 1.0f, 0.0f } + }; + // render glyph texture over quad + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, ch.textureID); + // update content of VBO memory + <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); + <function id='90'>glBufferSubData</function>(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0); + // render quad + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6); + // now advance cursors for next glyph (note that advance is number of 1/64 pixels) + x += (ch.Advance &gt;&gt; 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64) + } + <function id='27'>glBindVertexArray</function>(0); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, 0); +} +</code></pre> + +<p> + Most of the content of the function should be relatively self-explanatory: we first calculate the origin position of the quad (as <var>xpos</var> and <var>ypos</var>) and the quad's size (as <var>w</var> and <var>h</var>) and generate a set of 6 vertices to form the 2D quad; note that we scale each metric by <var>scale</var>. We then update the content of the VBO and render the quad. +</p> + +<p> + The following line of code requires some extra attention though: +</p> + +<pre><code> +float ypos = y - (ch.Size.y - ch.Bearing.y); +</code></pre> + +<p> + Some characters (like 'p' or 'g') are rendered slightly below the baseline, so the quad should also be positioned slightly below <fun>RenderText</fun>'s <var>y</var> value. The exact amount we need to offset <var>ypos</var> below the baseline can be figured out from the glyph metrics: +</p> + +<img src="/img/in-practice/glyph_offset.png" alt="Offset below baseline of glyph to position 2D quad"/> + +<p> + To calculate this distance e.g. offset we need to figure out the distance a glyph extends below the baseline; this distance is indicated by the red arrow. As you can see from the glyph metrics, we can calculate the length of this vector by subtracting <code>bearingY</code> from the glyph's (bitmap) height. This value is then <code>0.0</code> for characters that rest on the baseline (like 'X') and positive for characters that reside slightly below the baseline (like 'g' or 'j'). +</p> + +<p> + If you did everything correct you should now be able to successfully render strings of text with the following statements: +</p> + +<pre><code> +RenderText(shader, "This is sample text", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f)); +RenderText(shader, "(C) LearnOpenGL.com", 540.0f, 570.0f, 0.5f, glm::vec3(0.3, 0.7f, 0.9f)); +</code></pre> + +<p> + This should then look similar to the following image: +</p> + + <img src="/img/in-practice/text_rendering.png" class="clean" alt="Image of text rendering with OpenGL using FreeType"/> + +<p> + You can find the code of this example <a href="/code_viewer_gh.php?code=src/7.in_practice/2.text_rendering/text_rendering.cpp" target="_blank">here</a>. +</p> + +<p> + To give you a feel for how we calculated the quad's vertices, we can disable blending to see what the actual rendered quads look like: +</p> + + <img src="/img/in-practice/text_rendering_quads.png" class="clean" alt="Image of quads without transparency for text rendering in OpenGL"/> + +<p> + Here you can clearly see most quads resting on the (imaginary) baseline while the quads that corresponds to glyphs like 'p' or '(' are shifted downwards. +</p> + +<h2>Going further</h2> +<p> + This chapter demonstrated a text rendering technique with TrueType fonts using the FreeType library. The approach is flexible, scalable, and works with many character encodings. However, this approach is likely going to be overkill for your application as we generate and render textures for each glyph. Performance-wise, bitmap fonts are preferable as we only need one texture for all our glyphs. The best approach would be to combine the two approaches by dynamically generating a bitmap font texture featuring all character glyphs as loaded with FreeType. This saves the renderer from a significant amount of texture switches and, based on how tight each glyph is packed, could save quite some performance. +</p> + +<p> + Another issue with FreeType font bitmaps is that the glyph textures are stored with a fixed font size, so a significant amount of scaling may introduce jagged edges. Furthermore, rotations applied to the glyphs will cause them to appear blurry. This can be mitigated by, instead of storing the actual rasterized pixel colors, storing the distance to the closest glyph outline per pixel. This technique is called <def>signed distance field fonts</def> and Valve published a <a href="https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf" target="_blank">paper</a> a few years ago about their implementation of this technique which works surprisingly well for 3D rendering applications. +</p> + +<h2>Further reading</h2> +<ul> + <li><a href="https://www.websiteplanet.com/blog/best-free-fonts/" target="_blank">70+ Best Free Fonts for Designers</a>: summarized list of a large group of fonts to use in your project for personal or commercial use.</li> +</ul> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Introduction.html b/pub/Introduction.html @@ -0,0 +1,410 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Introduction</h1> + <h1 id="content-title">はじめに</h1> +<h1 id="content-url" style='display:none;'>Introduction</h1> +<p> + Since you came here you probably want to learn the inner workings of computer graphics and do all the stuff the cool kids do by yourself. Doing things by yourself is extremely fun and resourceful and gives you a great understanding of graphics programming. However, there are a few items that need to be taken into consideration before starting your journey. +ここに来たあなたはコンピュータグラフィックスの内部に興味があり、イケてるエンジニアがやっているようなことを自身の手ででも行いたいと考えていることでしょう。グラフィックプログラミングを自身の手で行うことはとても楽しく、頭脳を刺激されるものであり、またグラフィックプログラミングに関する深い理解が得られます。しかし旅に出掛ける前に考慮すべきことがいくつかあります。 +</p> + +<h2>Prerequisites</h2> +<h2>前提知識</h2> +<p> + Since OpenGL is a graphics API and not a platform of its own, it requires a language to operate in and the language of choice is <code>C++</code>. Therefore a decent knowledge of the <code>C++</code> programming language is required for these chapters. However, I will try to explain most of the concepts used, including advanced <code>C++</code> topics where required so it is not required to be an expert in <code>C++</code>, but you should be able to write more than just a <code>'Hello World'</code> program. If you don't have much experience with <code>C++</code> I can recommend the free tutorials at <a href="http://www.learncpp.com" target="_blank">www.learncpp.com</a>. +今日のOpenGLはグラフィックスAPIであり、プラットフォームではありません。そのためOpenGLを操作するためのプログラミング言語が必要です。本書ではそのような言語として<code>C++</code>を用います。そのため本書を読むにあたって<code>C++</code>の十分な知識が必要です。ときには<code>C++</code>の発展的な手法を用います。しかし可能な限り基本となる考え方は説明しますので、<code>C++</code>のエキスパートである必要はありません。ただし<code>'Hello World'</code>を出力するプログラムを書けるだけでは不十分です。<code>C++</code>の経験が不十分であれば、<a href="http://www.learncpp.com" target="_blank">www.learncpp.com</a>で利用できる無料のチュートリアルがおすすめです。 +</p> + +<p> + Also, we will be using some math (linear algebra, geometry, and trigonometry) along the way and I will try to explain all the required concepts of the math required. However, I'm not a mathematician by heart so even though my explanations may be easy to understand, they will most likely be incomplete. So where necessary I will provide pointers to good resources that explain the material in a more complete fashion. Don't be scared about the mathematical knowledge required before starting your journey into OpenGL; almost all the concepts can be understood with a basic mathematical background and I will try to keep the mathematics to a minimum where possible. Most of the functionality doesn't even require you to understand all the math as long as you know how to use it. +加えて数学も利用します。線形代数、幾何学、三角関数といったものです。これらの数学についても、必要な概念はできるだけ説明するようにします。ただし私は数学者ではありません。私の説明が理解しやすかったとしても、その説明は数学的に完全なものではないかもしれません。そのため必要に応じてより完全な説明がされている情報源を提供するようにします。OpenGLの旅に出掛けていない段階で数学に恐れをなさないでください。ほとんどの概念は基本的な数学の知識があれば理解できますし、数学の利用自体も最小限にとどめるように努力します。ほとんどの場合、使い方だけ知っていれば、数学の中身を理解する必要さえありません。 +</p> + +<h2>Structure</h2> +<h2>本書の構成</h2> +<p> + LearnOpenGL is broken down into a number of general sections. Each section contains several chapters that each explain different concepts in large detail. Each of the chapters can be found at the menu to your left. The concepts are taught in a linear fashion (so it is advised to start from the top to the bottom, unless otherwise instructed) where each chapter explains the background theory and the practical aspects. +本書はいくつかの節におおまかに分けられます。各節は個々の概念を詳細に解説した章により構成されます。各章は左のメニューから見つけることができます。個々の概念は順番に解説していますので、特に断りがなければ前から順番に読むことを推奨します。各章では背景となる理屈と実際の利用法を説明します。 +</p> + +<p> + To make the concepts easier to follow, and give them some added structure, the book contains <em>boxes</em>, <em>code blocks</em>, <em>color hints</em> and <em>function references</em>. +本書を視覚的に見やすくし、話の流れを追跡しやすくするため、いくつかの<em>ボックス</em>、<em>コードブロック</em>、<em>色付き文字</em>そして<em>関数の照会</em>を用います。 +</p> + +<h3>Boxes</h3> +<h3>ボックス</h3> +<note><strong>Green</strong> boxes encompasses some notes or useful features/hints about OpenGL or the subject at hand.</note> +<note><strong>緑</strong>のボックスはOpenGLについてあるいはその場所で議論している話題についてのメモや有用な特性、ヒントを含みます。</note> +<warning><strong>Red</strong> boxes will contain warnings or other features you have to be extra careful with.</warning> +<warning><strong>赤</strong>のボックスは警告や、十分な注意をはらうべき特性を含みます。</warning> + +<h3>Code</h3> +<h3>コード</h3> +<p> + You will find plenty of small pieces of code in the website that are located in dark-gray boxes with syntax-highlighted code as you can see below: +本書にはたくさんのコードがでてきますが、それらは以下のような深い灰色のボックスに、シンタックスハイライトがつけられて記述されています: +</p> + +<pre><code> +// This box contains code +</code></pre> +<pre><code> +// このボックスはコードを含みます。 +</code></pre> + + <p> + Since these provide only snippets of code, wherever necessary I will provide a link to the entire source code required for a given subject. +このようなコードは断片であるため、必要に応じてコード全体が記述されているリンクを貼っておきます。 +</p> + +<h3>Color hints</h3> +<h3>色付き文字</h3> +<p> + Some words are displayed with a different color to make it extra clear these words portray a special meaning: +一部の文字には色が付けられ、特別な意味があることを見やすくしています: +</p> + +<ul> + <li><def>Definition</def>: green words specify a definition i.e. an important aspect/name of something you're likely to hear more often.</li> + <li><def>定義</def>: 緑の文字は定義をあらわします。これらは以降の文章で頻繁に見られる概念や名前です。 + <li><fun>Program structure</fun>: red words specify function names or class names.</li> + <li><fun>プログラムの構造</fun>: 赤い文字は関数やクラスの名前をあらわします。 + <li><var>Variables</var>: blue words specify variables including all OpenGL constants.</li> + <li><var>変数</var>: 青い文字はOpenGLのすべての定数を含む変数をあらわします。 +</ul> + +<h3>OpenGL Function references</h3> +<h3>OpenGL関数の照会</h3> +<p> + A particularly well appreciated feature of LearnOpenGL is the ability to review most of OpenGL's functions wherever they show up in the content. Whenever a function is found in the content that is documented at the website, the function will show up with a slightly noticeable underline. You can hover the mouse over the function and after a small interval, a pop-up window will show relevant information about this function including a nice overview of what the function actually does. Hover your mouse over <fun><function id='60'>glEnable</function></fun> to see it in action. +この本がひろく受け容れられている特徴として、文中にあらわれたOpenGLの関数をいつでも照会できることがあげられます。文中の関数にはよく見ると下線が引かれています。マウスのカーソルを関数の上にあわせてしばらくすると、ポップアップがでて関数の情報が表示されます。こちらの関数<fun><function id='60'>glEnable</function></fun>で試してみてください。 +</p> + +<p> + Now that you got a bit of a feel of the structure of the site, hop over to the Getting Started section to start your journey in OpenGL! +以上で本書の構成はおおまかにつかめたかと思います。本編に進み、OpenGLの旅へ出掛けましょう。 +</p> + + </div> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/Lighting/Basic-Lighting.html b/pub/Lighting/Basic-Lighting.html @@ -0,0 +1,712 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Basic Lighting</h1> +<h1 id="content-url" style='display:none;'>Lighting/Basic-Lighting</h1> +<p> + Lighting in the real world is extremely complicated and depends on way too many factors, something we can't afford to calculate on the limited processing power we have. Lighting in OpenGL is therefore based on approximations of reality using simplified models that are much easier to process and look relatively similar. These lighting models are based on the physics of light as we understand it. One of those models is called the <def>Phong lighting model</def>. The major building blocks of the Phong lighting model consist of 3 components: ambient, diffuse and specular lighting. Below you can see what these lighting components look like on their own and combined: +</p> + +<img src="/img/lighting/basic_lighting_phong.png"/> + +<p> + <ul> + <li><def>Ambient lighting</def>: even when it is dark there is usually still some light somewhere in the world (the moon, a distant light) so objects are almost never completely dark. To simulate this we use an ambient lighting constant that always gives the object some color.</li> + <li><def>Diffuse lighting</def>: simulates the directional impact a light object has on an object. This is the most visually significant component of the lighting model. The more a part of an object faces the light source, the brighter it becomes.</li> + <li><def>Specular lighting</def>: simulates the bright spot of a light that appears on shiny objects. Specular highlights are more inclined to the color of the light than the color of the object.</li> + </ul> +</p> + +<p> + To create visually interesting scenes we want to at least simulate these 3 lighting components. We'll start with the simplest one: <em>ambient lighting</em>. +</p> + +<h1>Ambient lighting</h1> +<p> + Light usually does not come from a single light source, but from many light sources scattered all around us, even when they're not immediately visible. One of the properties of light is that it can scatter and bounce in many directions, reaching spots that aren't directly visible; light can thus <em>reflect</em> on other surfaces and have an indirect impact on the lighting of an object. Algorithms that take this into consideration are called <def>global illumination</def> algorithms, but these are complicated and expensive to calculate. +</p> + +<p> + Since we're not big fans of complicated and expensive algorithms we'll start by using a very simplistic model of global illumination, namely <def>ambient lighting</def>. As you've seen in the previous section we use a small constant (light) color that we add to the final resulting color of the object's fragments, thus making it look like there is always some scattered light even when there's not a direct light source. +</p> + +<p> + Adding ambient lighting to the scene is really easy. We take the light's color, multiply it with a small constant ambient factor, multiply this with the object's color, and use that as the fragment's color in the cube object's shader: +</p> + +<pre><code> +void main() +{ + float ambientStrength = 0.1; + vec3 ambient = ambientStrength * lightColor; + + vec3 result = ambient * objectColor; + FragColor = vec4(result, 1.0); +} +</code></pre> + +<p> + If you'd now run the program, you'll notice that the first stage of lighting is now successfully applied to the object. The object is quite dark, but not completely since ambient lighting is applied (note that the light cube is unaffected because we use a different shader). It should look something like this: +</p> + +<img src="/img/lighting/ambient_lighting.png" class="clean"/> + +<h1>Diffuse lighting</h1> +<p> + Ambient lighting by itself doesn't produce the most interesting results, but diffuse lighting however will start to give a significant visual impact on the object. Diffuse lighting gives the object more brightness the closer its fragments are aligned to the light rays from a light source. To give you a better understanding of diffuse lighting take a look at the following image: +</p> + +<img src="/img/lighting/diffuse_light.png" class="clean"/> + +<p> + To the left we find a light source with a light ray targeted at a single fragment of our object. We need to measure at what angle the light ray touches the fragment. If the light ray is perpendicular to the object's surface the light has the greatest impact. To measure the angle between the light ray and the fragment we use something called a <def>normal vector</def>, that is a vector perpendicular to the fragment's surface (here depicted as a yellow arrow); we'll get to that later. The angle between the two vectors can then easily be calculated with the dot product. +</p> + +<p> + You may remember from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter that, the lower the angle between two unit vectors, the more the dot product is inclined towards a value of 1. When the angle between both vectors is 90 degrees, the dot product becomes 0. The same applies to \(\theta\): the larger \(\theta\) becomes, the less of an impact the light should have on the fragment's color. +</p> + +<note> + Note that to get (only) the cosine of the angle between both vectors we will work with <em>unit vectors</em> (vectors of length <code>1</code>) so we need to make sure all the vectors are normalized, otherwise the dot product returns more than just the cosine (see <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">Transformations</a>). +</note> + +<p> + The resulting dot product thus returns a scalar that we can use to calculate the light's impact on the fragment's color, resulting in differently lit fragments based on their orientation towards the light. +</p> + +<p> + So, what do we need to calculate diffuse lighting: + <ul> + <li>Normal vector: a vector that is perpendicular to the vertex' surface.</li> + <li>The directed light ray: a direction vector that is the difference vector between the light's position and the fragment's position. To calculate this light ray we need the light's position vector and the fragment's position vector.</li> + </ul> +</p> + +<h2>Normal vectors</h2> +<p> + A normal vector is a (unit) vector that is perpendicular to the surface of a vertex. Since a vertex by itself has no surface (it's just a single point in space) we retrieve a normal vector by using its surrounding vertices to figure out the surface of the vertex. We can use a little trick to calculate the normal vectors for all the cube's vertices by using the cross product, but since a 3D cube is not a complicated shape we can simply manually add them to the vertex data. The updated vertex data array can be found <a href="/code_viewer.php?code=lighting/basic_lighting_vertex_data" target="_blank">here</a>. Try to visualize that the normals are indeed vectors perpendicular to each plane's surface (a cube consists of 6 planes). +</p> + +<p> + Since we added extra data to the vertex array we should update the cube's vertex shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +... +</code></pre> + +<p> + Now that we added a normal vector to each of the vertices and updated the vertex shader we should update the vertex attribute pointers as well. Note that the light source's cube uses the same vertex array for its vertex data, but the lamp shader has no use of the newly added normal vectors. We don't have to update the lamp's shaders or attribute configurations, but we have to at least modify the vertex attribute pointers to reflect the new vertex array's size: +</p> + +<pre><code> +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +</code></pre> + +<p> + We only want to use the first <code>3</code> floats of each vertex and ignore the last <code>3</code> floats so we only need to update the <em>stride</em> parameter to <code>6</code> times the size of a <code>float</code> and we're done. +</p> + +<note> + It may look inefficient using vertex data that is not completely used by the lamp shader, but the vertex data is already stored in the GPU's memory from the container object so we don't have to store new data into the GPU's memory. This actually makes it more efficient compared to allocating a new VBO specifically for the lamp. +</note> + +<p> + All the lighting calculations are done in the fragment shader so we need to forward the normal vectors from the vertex shader to the fragment shader. Let's do that: +</p> + +<pre><code> +out vec3 Normal; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + Normal = aNormal; +} +</code></pre> + +<p> + What's left to do is declare the corresponding input variable in the fragment shader: +</p> + +<pre><code> +in vec3 Normal; +</code></pre> + +<h2>Calculating the diffuse color</h2> +<p> + We now have the normal vector for each vertex, but we still need the light's position vector and the fragment's position vector. Since the light's position is a single static variable we can declare it as a uniform in the fragment shader: +</p> + +<pre><code> +uniform vec3 lightPos; +</code></pre> + +<p> + And then update the uniform in the render loop (or outside since it doesn't change per frame). We use the <var>lightPos</var> vector declared in the previous chapter as the location of the diffuse light source: +</p> + +<pre><code> +lightingShader.setVec3("lightPos", lightPos); +</code></pre> + +<p> + Then the last thing we need is the actual fragment's position. We're going to do all the lighting calculations in world space so we want a vertex position that is in world space first. We can accomplish this by multiplying the vertex position attribute with the model matrix only (not the view and projection matrix) to transform it to world space coordinates. This can easily be accomplished in the vertex shader so let's declare an output variable and calculate its world space coordinates: +</p> + +<pre><code> +out vec3 FragPos; +out vec3 Normal; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = aNormal; +} +</code></pre> + +<p> + And lastly add the corresponding input variable to the fragment shader: +</p> + +<pre><code> +in vec3 FragPos; +</code></pre> + +<p> + This <code>in</code> variable will be interpolated from the 3 world position vectors of the triangle to form the <var>FragPos</var> vector that is the per-fragment world position. Now that all the required variables are set we can start the lighting calculations. +</p> + + +<p> + The first thing we need to calculate is the direction vector between the light source and the fragment's position. From the previous section we know that the light's direction vector is the difference vector between the light's position vector and the fragment's position vector. As you may remember from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter we can easily calculate this difference by subtracting both vectors from each other. We also want to make sure all the relevant vectors end up as unit vectors so we normalize both the normal and the resulting direction vector: +</p> + +<pre><code> +vec3 norm = normalize(Normal); +vec3 lightDir = normalize(lightPos - FragPos); +</code></pre> + +<note> + When calculating lighting we usually do not care about the magnitude of a vector or their position; we only care about their direction. Because we only care about their direction almost all the calculations are done with unit vectors since it simplifies most calculations (like the dot product). So when doing lighting calculations, make sure you always normalize the relevant vectors to ensure they're actual unit vectors. Forgetting to normalize a vector is a popular mistake. +</note> + +<p> + Next we need to calculate the diffuse impact of the light on the current fragment by taking the dot product between the <var>norm</var> and <var>lightDir</var> vectors. The resulting value is then multiplied with the light's color to get the diffuse component, resulting in a darker diffuse component the greater the angle between both vectors: +</p> + +<pre><code> +float diff = max(dot(norm, lightDir), 0.0); +vec3 diffuse = diff * lightColor; +</code></pre> + +<p> + If the angle between both vectors is greater than <code>90</code> degrees then the result of the dot product will actually become negative and we end up with a negative diffuse component. + For that reason we use the <fun>max</fun> function that returns the highest of both its parameters to make sure the diffuse component (and thus the colors) never become negative. Lighting for negative colors is not really defined so it's best to stay away from that, unless you're one of those eccentric artists. +</p> + +<p> + Now that we have both an ambient and a diffuse component we add both colors to each other and then multiply the result with the color of the object to get the resulting fragment's output color: +</p> + +<pre><code> +vec3 result = (ambient + diffuse) * objectColor; +FragColor = vec4(result, 1.0); +</code></pre> + +<p> + If your application (and shaders) compiled successfully you should see something like this: +</p> + +<img src="/img/lighting/basic_lighting_diffuse.png" class="clean"/> + +<p> + You can see that with diffuse lighting the cube starts to look like an actual cube again. Try visualizing the normal vectors in your head and move the camera around the cube to see that the larger the angle between the normal vector and the light's direction vector, the darker the fragment becomes. +</p> + +<p> + Feel free to compare your source code with the complete source code <a href="/code_viewer_gh.php?code=src/2.lighting/2.1.basic_lighting_diffuse/basic_lighting_diffuse.cpp" target="_blank">here</a> if you're stuck. +</p> + +<h2>One last thing</h2> +<p> + in the previous section we passed the normal vector directly from the vertex shader to the fragment shader. However, the calculations in the fragment shader are all done in world space, so shouldn't we transform the normal vectors to world space coordinates as well? Basically yes, but it's not as simple as simply multiplying it with a model matrix. +</p> + +<p> + First of all, normal vectors are only direction vectors and do not represent a specific position in space. Second, normal vectors do not have a homogeneous coordinate (the <code>w</code> component of a vertex position). This means that translations should not have any effect on the normal vectors. So if we want to multiply the normal vectors with a model matrix we want to remove the translation part of the matrix by taking the upper-left <code>3x3</code> matrix of the model matrix (note that we could also set the <code>w</code> component of a normal vector to <code>0</code> and multiply with the 4x4 matrix). +</p> + +<p> + Second, if the model matrix would perform a non-uniform scale, the vertices would be changed in such a way that the normal vector is not perpendicular to the surface anymore. The following image shows the effect such a model matrix (with non-uniform scaling) has on a normal vector: +</p> + +<img src="/img/lighting/basic_lighting_normal_transformation.png" class="clean"/> + +<p> + Whenever we apply a non-uniform scale (note: a uniform scale only changes the normal's magnitude, not its direction, which is easily fixed by normalizing it) the normal vectors are not perpendicular to the corresponding surface anymore which distorts the lighting. +</p> + +<p> + The trick of fixing this behavior is to use a different model matrix specifically tailored for normal vectors. This matrix is called the <def>normal matrix</def> and uses a few linear algebraic operations to remove the effect of wrongly scaling the normal vectors. If you want to know how this matrix is calculated I suggest the following <a href="http://www.lighthouse3d.com/tutorials/glsl-tutorial/the-normal-matrix/" target="_blank">article</a>. +</p> + +<p> + The normal matrix is defined as 'the transpose of the inverse of the upper-left 3x3 part of the model matrix'. Phew, that's a mouthful and if you don't really understand what that means, don't worry; we haven't discussed inverse and transpose matrices yet. Note that most resources define the normal matrix as derived from the model-view matrix, but since we're working in world space (and not in view space) we will derive it from the model matrix. +</p> + +<p> + In the vertex shader we can generate the normal matrix by using the <fun>inverse</fun> and <fun>transpose</fun> functions in the vertex shader that work on any matrix type. Note that we cast the matrix to a 3x3 matrix to ensure it loses its translation properties and that it can multiply with the <code>vec3</code> normal vector: +</p> + +<pre><code> +Normal = mat3(transpose(inverse(model))) * aNormal; +</code></pre> + +<warning> + Inversing matrices is a costly operation for shaders, so wherever possible try to avoid doing inverse operations since they have to be done on each vertex of your scene. For learning purposes this is fine, but for an efficient application you'll likely want to calculate the normal matrix on the CPU and send it to the shaders via a uniform before drawing (just like the model matrix). +</warning> + +<p> + In the diffuse lighting section the lighting was fine because we didn't do any scaling on the object, so there was not really a need to use a normal matrix and we could've just multiplied the normals with the model matrix. If you are doing a non-uniform scale however, it is essential that you multiply your normal vectors with the normal matrix. +</p> + + +<h1>Specular Lighting</h1> +<p> + If you're not exhausted already by all the lighting talk we can start finishing the Phong lighting model by adding specular highlights. +</p> + +<p> + Similar to diffuse lighting, specular lighting is based on the light's direction vector and the object's normal vectors, but this time it is also based on the view direction e.g. from what direction the player is looking at the fragment. Specular lighting is based on the reflective properties of surfaces. If we think of the object's surface as a mirror, the specular lighting is the strongest wherever we would see the light reflected on the surface. You can see this effect in the following image: +</p> + +<img src="/img/lighting/basic_lighting_specular_theory.png" class="clean"/> + +<p> + We calculate a reflection vector by reflecting the light direction around the normal vector. Then we calculate the angular distance between this reflection vector and the view direction. The closer the angle between them, the greater the impact of the specular light. The resulting effect is that we see a bit of a highlight when we're looking at the light's direction reflected via the surface. +</p> + +<p> + The view vector is the one extra variable we need for specular lighting which we can calculate using the viewer's world space position and the fragment's position. Then we calculate the specular's intensity, multiply this with the light color and add this to the ambient and diffuse components. +</p> + +<note> + We chose to do the lighting calculations in world space, but most people tend to prefer doing lighting in view space. An advantage of view space is that the viewer's position is always at <code>(0,0,0)</code> so you already got the position of the viewer for free. However, I find calculating lighting in world space more intuitive for learning purposes. If you still want to calculate lighting in view space you want to transform all the relevant vectors with the view matrix as well (don't forget to change the normal matrix too). +</note> + +<p> + To get the world space coordinates of the viewer we simply take the position vector of the camera object (which is the viewer of course). So let's add another uniform to the fragment shader and pass the camera position vector to the shader: +</p> + +<pre><code> +uniform vec3 viewPos; +</code></pre> + +<pre><code> +lightingShader.setVec3("viewPos", camera.Position); +</code></pre> + +<p> + Now that we have all the required variables we can calculate the specular intensity. First we define a specular intensity value to give the specular highlight a medium-bright color so that it doesn't have too much of an impact: +</p> + +<pre><code> +float specularStrength = 0.5; +</code></pre> + +<p> + If we would set this to <code>1.0f</code> we'd get a really bright specular component which is a bit too much for a coral cube. In the <a href="https://learnopengl.com/Lighting/Materials" target="_blank">next</a> chapter we'll talk about properly setting all these lighting intensities and how they affect the objects. Next we calculate the view direction vector and the corresponding reflect vector along the normal axis: +</p> + +<pre><code> +vec3 viewDir = normalize(viewPos - FragPos); +vec3 reflectDir = reflect(-lightDir, norm); +</code></pre> + +<p> + Note that we negate the <code>lightDir</code> vector. The <code>reflect</code> function expects the first vector to point <strong>from</strong> the light source towards the fragment's position, but the <code>lightDir</code> vector is currently pointing the other way around: from the fragment <strong>towards</strong> the light source (this depends on the order of subtraction earlier on when we calculated the <code>lightDir</code> vector). To make sure we get the correct <code>reflect</code> vector we reverse its direction by negating the <code>lightDir</code> vector first. The second argument expects a normal vector so we supply the normalized <code>norm</code> vector. +</p> + +<p> + Then what's left to do is to actually calculate the specular component. This is accomplished with the following formula: + +<pre><code> +float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); +vec3 specular = specularStrength * spec * lightColor; +</code></pre> + +<p> + We first calculate the dot product between the view direction and the reflect direction (and make sure it's not negative) and then raise it to the power of <code>32</code>. This <code>32</code> value is the <def>shininess</def> value of the highlight. The higher the shininess value of an object, the more it properly reflects the light instead of scattering it all around and thus the smaller the highlight becomes. Below you can see an image that shows the visual impact of different shininess values: +</p> + +<img src="/img/lighting/basic_lighting_specular_shininess.png"/> + +<p> + We don't want the specular component to be too distracting so we keep the exponent at <code>32</code>. The only thing left to do is to add it to the ambient and diffuse components and multiply the combined result with the object's color: +</p> + +<pre><code> +vec3 result = (ambient + diffuse + specular) * objectColor; +FragColor = vec4(result, 1.0); +</code></pre> + +<p> + We now calculated all the lighting components of the Phong lighting model. Based on your point of view you should see something like this: +</p> + +<img src="/img/lighting/basic_lighting_specular.png" class="clean"/> + +<p> + You can find the complete source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/2.2.basic_lighting_specular/basic_lighting_specular.cpp" target="_blank">here</a>. +</p> + +<note> + <p> + In the earlier days of lighting shaders, developers used to implement the Phong lighting model in the vertex shader. The advantage of doing lighting in the vertex shader is that it is a lot more efficient since there are generally a lot less vertices compared to fragments, so the (expensive) lighting calculations are done less frequently. However, the resulting color value in the vertex shader is the resulting lighting color of that vertex only and the color values of the surrounding fragments are then the result of interpolated lighting colors. The result was that the lighting was not very realistic unless large amounts of vertices were used: + </p> + + <img src="/img/lighting/basic_lighting_gouruad.png"/> + + <p> + When the Phong lighting model is implemented in the vertex shader it is called <def>Gouraud shading</def> instead of <def>Phong shading</def>. Note that due to the interpolation the lighting looks somewhat off. The Phong shading gives much smoother lighting results. + </p> +</note> + +<p> + By now you should be starting to see just how powerful shaders are. With little information shaders are able to calculate how lighting affects the fragment's colors for all our objects. In the <a href="https://learnopengl.com/Lighting/Materials" target="_blank">next</a> chapters we'll be delving much deeper into what we can do with the lighting model. +</p> + +<h2>Exercises</h2> +<ul> + <li>Right now the light source is a boring static light source that doesn't move. Try to move the light source around the scene over time using either <fun>sin</fun> or <fun>cos</fun>. Watching the lighting change over time gives you a good understanding of Phong's lighting model: <a href="/code_viewer_gh.php?code=src/2.lighting/2.3.basic_lighting_exercise1/basic_lighting_exercise1.cpp" target="_blank">solution</a>. </li> + <li>Play around with different ambient, diffuse and specular strengths and see how they impact the result. Also experiment with the shininess factor. Try to comprehend why certain values have a certain visual output.</li> + <li>Do Phong shading in view space instead of world space: <a href="/code_viewer_gh.php?code=src/2.lighting/2.4.basic_lighting_exercise2/basic_lighting_exercise2.cpp" target="_blank">solution</a>. </li> + <li>Implement Gouraud shading instead of Phong shading. If you did things right the lighting should <a href="/img/lighting/basic_lighting_exercise3.png" target="_blank">look a bit off</a> (especially the specular highlights) with the cube object. Try to reason why it looks so weird: <a href="/code_viewer_gh.php?code=src/2.lighting/2.5.basic_lighting_exercise3/basic_lighting_exercise3.cpp" target="_blank">solution</a>.</li> +</ul> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Colors.html b/pub/Lighting/Colors.html @@ -0,0 +1,542 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Colors</h1> +<h1 id="content-url" style='display:none;'>Lighting/Colors</h1> +<p> + We briefly used and manipulated colors in the previous chapters, but never defined them properly. Here we'll discuss what colors are and start building the scene for the upcoming Lighting chapters. +</p> + +<p> + In the real world, colors can take any known color value with each object having its own color(s). In the digital world we need to map the (infinite) real colors to (limited) digital values and therefore not all real-world colors can be represented digitally. Colors are digitally represented using a <code>red</code>, <code>green</code> and <code>blue</code> component commonly abbreviated as <code>RGB</code>. Using different combinations of just those 3 values, within a range of <code>[0,1]</code>, we can represent almost any color there is. For example, to get a <em>coral</em> color, we define a color vector as: +</p> + +<pre><code> +glm::vec3 coral(1.0f, 0.5f, 0.31f); +</code></pre> + +<p> + The color of an object we see in real life is not the color it actually has, but is the color <def>reflected</def> from the object. The colors that aren't absorbed (rejected) by the object is the color we perceive of it. As an example, the light of the sun is perceived as a white light that is the combined sum of many different colors (as you can see in the image). If we would shine this white light on a blue toy, it would absorb all the white color's sub-colors except the blue color. Since the toy does not absorb the blue color part, it is reflected. This reflected light enters our eye, making it look like the toy has a blue color. The following image shows this for a coral colored toy where it reflects several colors with varying intensity: +</p> + +<img src="/img/lighting/light_reflection.png" class="clean"/> + +<p> + You can see that the white sunlight is a collection of all the visible colors and the object absorbs a large portion of those colors. It only reflects those colors that represent the object's color and the combination of those is what we perceive (in this case a coral color). +</p> + +<note> + Technically it's a bit more complicated, but we'll get to that in the PBR chapters. +</note> + +<p> + These rules of color reflection apply directly in graphics-land. When we define a light source in OpenGL we want to give this light source a color. In the previous paragraph we had a white color so we'll give the light source a white color as well. If we would then multiply the light source's color with an object's color value, the resulting color would be the reflected color of the object (and thus its perceived color). Let's revisit our toy (this time with a coral value) and see how we would calculate its perceived color in graphics-land. We get the resulting color vector by doing a component-wise multiplication between the light and object color vectors: +</p> + +<pre><code> +glm::vec3 lightColor(1.0f, 1.0f, 1.0f); +glm::vec3 toyColor(1.0f, 0.5f, 0.31f); +glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f); +</code></pre> + +<p> + We can see that the toy's color <em>absorbs</em> a large portion of the white light, but reflects several red, green and blue values based on its own color value. This is a representation of how colors would work in real life. We can thus define an object's color as <em>the amount of each color component it reflects from a light source</em>. Now what would happen if we used a green light? +</p> + +<pre><code> +glm::vec3 lightColor(0.0f, 1.0f, 0.0f); +glm::vec3 toyColor(1.0f, 0.5f, 0.31f); +glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f); +</code></pre> + +<p> + As we can see, the toy has no red and blue light to absorb and/or reflect. The toy also absorbs half of the light's green value, but also reflects half of the light's green value. The toy's color we perceive would then be a dark-greenish color. We can see that if we use a green light, only the green color components can be reflected and thus perceived; no red and blue colors are perceived. As a result the coral object suddenly becomes a dark-greenish object. Let's try one more example with a dark olive-green light: +</p> + +<pre><code> +glm::vec3 lightColor(0.33f, 0.42f, 0.18f); +glm::vec3 toyColor(1.0f, 0.5f, 0.31f); +glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f); +</code></pre> + +<p> + As you can see, we can get interesting colors from objects using different light colors. It's not hard to get creative with colors. +</p> + +<p> + But enough about colors, let's start building a scene where we can experiment in. +</p> + +<h1>A lighting scene</h1> +<p> + In the upcoming chapters we'll be creating interesting visuals by simulating real-world lighting making extensive use of colors. Since now we'll be using light sources we want to display them as visual objects in the scene and add at least one object to simulate the lighting from. +</p> + +<p> + The first thing we need is an object to cast the light on and we'll use the infamous container cube from the previous chapters. We'll also be needing a light object to show where the light source is located in the 3D scene. For simplicity's sake we'll represent the light source with a cube as well (we already have the <a href="https://learnopengl.com/code_viewer.php?code=getting-started/cube_vertices_pos" target="_blank">vertex data</a> right?). +</p> + +<p> + So, filling a vertex buffer object, setting vertex attribute pointers and all that jazz should be familiar for you by now so we won't walk you through those steps. If you still have no idea what's going on with those I suggest you review the <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">previous chapters</a>, and work through the exercises if possible, before continuing. +</p> + +<p> + So, the first thing we'll need is a vertex shader to draw the container. The vertex positions of the container remain the same (although we won't be needing texture coordinates this time) so the code should be nothing new. We'll be using a stripped down version of the vertex shader from the last chapters: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); +} +</code></pre> + +<p> + Make sure to update the vertex data and attribute pointers to match the new vertex shader (if you want, you can actually keep the texture data and attribute pointers active; we're just not using them right now). +</p> + +<p> + Because we're also going to render a light source cube, we want to generate a new VAO specifically for the light source. We could render the light source with the same VAO and then do a few light position transformations on the <var>model</var> matrix, but in the upcoming chapters we'll be changing the vertex data and attribute pointers of the container object quite often and we don't want these changes to propagate to the light source object (we only care about the light cube's vertex positions), so we'll create a new VAO: +</p> + +<pre><code> +unsigned int lightVAO; +<function id='33'>glGenVertexArrays</function>(1, &lightVAO); +<function id='27'>glBindVertexArray</function>(lightVAO); +// we only need to bind to the VBO, the container's VBO's data already contains the data. +<function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); +// set the vertex attribute +<function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); +<function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); +</code></pre> + +<p> + The code should be relatively straightforward. Now that we created both the container and the light source cube there is one thing left to define and that is the fragment shader for both the container and the light source: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +uniform vec3 objectColor; +uniform vec3 lightColor; + +void main() +{ + FragColor = vec4(lightColor * objectColor, 1.0); +} +</code></pre> + +<p> + The fragment shader accepts both an object color and a light color from a uniform variable. Here we multiply the light's color with the object's (reflected) color like we discussed at the beginning of this chapter. Again, this shader should be easy to understand. Let's set the object's color to the last section's coral color with a white light: +</p> + +<pre><code> +// don't forget to use the corresponding shader program first (to set the uniform) +lightingShader.use(); +lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f); +lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f); +</code></pre> + +<p> + One thing left to note is that when we start to update these <em>lighting shaders</em> in the next chapters, the light source cube would also be affected and this is not what we want. We don't want the light source object's color to be affected the lighting calculations, but rather keep the light source isolated from the rest. We want the light source to have a constant bright color, unaffected by other color changes (this makes it look like the light source cube really is the source of the light). +</p> + +<p> + To accomplish this we need to create a second set of shaders that we'll use to draw the light source cube, thus being safe from any changes to the lighting shaders. The vertex shader is the same as the lighting vertex shader so you can simply copy the source code over. The fragment shader of the light source cube ensures the cube's color remains bright by defining a constant white color on the lamp: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); // set all 4 vector values to 1.0 +} +</code></pre> + +<p> + When we want to render, we want to render the container object (or possibly many other objects) using the lighting shader we just defined, and when we want to draw the light source we use the light source's shaders. During the Lighting chapters we'll gradually be updating the lighting shaders to slowly achieve more realistic results. +</p> + + <p> + The main purpose of the light source cube is to show where the light comes from. We usually define a light source's position somewhere in the scene, but this is simply a position that has no visual meaning. To show where the light source actually is we render a cube at the same location of the light source. We render this cube with the light source cube shader to make sure the cube always stays white, regardless of the light conditions of the scene. +</p> + +<p> + So let's declare a global <code>vec3</code> variable that represents the light source's location in world-space coordinates: +</p> + +<pre><code> +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); +</code></pre> + +<p> + We then translate the light source cube to the light source's position and scale it down before rendering it: +</p> + +<pre><code> +model = glm::mat4(1.0f); +model = <function id='55'>glm::translate</function>(model, lightPos); +model = <function id='56'>glm::scale</function>(model, glm::vec3(0.2f)); +</code></pre> + +<p> + The resulting render code for the light source cube should then look something like this: +</p> + +<pre><code> +lightCubeShader.use(); +// set the model, view and projection matrix uniforms +[...] +// draw the light cube object +<function id='27'>glBindVertexArray</function>(lightCubeVAO); +<function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +</code></pre> + +<p> + Injecting all the code fragments at their appropriate locations would then result in a clean OpenGL application properly configured for experimenting with lighting. If everything compiles it should look like this: +</p> + +<img src="/img/lighting/colors_scene.png" class="clean"/> + +<p> + Not really much to look at right now, but I'll promise it'll get more interesting in the upcoming chapters. +</p> + +<p> + If you have difficulties finding out where all the code snippets fit together in the application as a whole, check the source code <a href="/code_viewer_gh.php?code=src/2.lighting/1.colors/colors.cpp" target="_blank">here</a> and carefully work your way through the code/comments. +</p> + +<p> + Now that we have a fair bit of knowledge about colors and created a basic scene for experimenting with lighting we can jump to the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">next</a> chapter where the real magic begins. +</p> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Light-casters.html b/pub/Lighting/Light-casters.html @@ -0,0 +1,869 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Light casters</h1> +<h1 id="content-url" style='display:none;'>Lighting/Light-casters</h1> +<p> + All the lighting we've used so far came from a single source that is a single point in space. It gives good results, but in the real world we have several types of light that each act different. A light source that <em>casts</em> light upon objects is called a <def>light caster</def>. In this chapter we'll discuss several different types of light casters. Learning to simulate different light sources is yet another tool in your toolbox to further enrich your environments. +</p> + +<p> + We'll first discuss a directional light, then a point light which is an extension of what we had before, and lastly we'll discuss spotlights. In the <a href="https://learnopengl.com/Lighting/Multiple-lights" target="_blank">next</a> chapter we'll combine several of these different light types into one scene. +</p> + +<h1>Directional Light</h1> +<p> + When a light source is far away the light rays coming from the light source are close to parallel to each other. It looks like all the light rays are coming from the same direction, regardless of where the object and/or the viewer is. When a light source is modeled to be <em>infinitely</em> far away it is called a <def>directional light</def> since all its light rays have the same direction; it is independent of the location of the light source. +</p> + +<p> + A fine example of a directional light source is the sun as we know it. The sun is not infinitely far away from us, but it is so far away that we can perceive it as being infinitely far away in the lighting calculations. All the light rays from the sun are then modeled as parallel light rays as we can see in the following image: +</p> + + <img src="/img/lighting/light_casters_directional.png" class="clean"/> + +<p> + Because all the light rays are parallel it does not matter how each object relates to the light source's position since the light direction remains the same for each object in the scene. Because the light's direction vector stays the same, the lighting calculations will be similar for each object in the scene. + </p> + +<p> + We can model such a directional light by defining a light direction vector instead of a position vector. The shader calculations remain mostly the same except this time we directly use the light's <var>direction</var> vector instead of calculating the <var>lightDir</var> vector using the light's <var>position</var> vector: +</p> + +<pre><code> +struct Light { + // vec3 position; // no longer necessary when using directional lights. + vec3 direction; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; +[...] +void main() +{ + vec3 lightDir = normalize(-light.direction); + [...] +} +</code></pre> + + <p> + Note that we first negate the <var>light.direction</var> vector. The lighting calculations we used so far expect the light direction to be a direction from the fragment <strong>towards</strong> the light source, but people generally prefer to specify a directional light as a global direction pointing <strong>from</strong> the light source. Therefore we have to negate the global light direction vector to switch its direction; it's now a direction vector pointing towards the light source. Also, be sure to normalize the vector since it is unwise to assume the input vector to be a unit vector. + </p> + +<p> + The resulting <var>lightDir</var> vector is then used as before in the diffuse and specular computations. +</p> + +<p> + To clearly demonstrate that a directional light has the same effect on multiple objects we revisit the container party scene from the end of the <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">Coordinate systems</a> chapter. In case you missed the party we defined 10 different <a href="/code_viewer.php?code=lighting/light_casters_container_positions" target="_blank">container positions</a> and generated a different model matrix per container where each model matrix contained the appropriate local-to-world transformations: +</p> + +<pre><code> +for(unsigned int i = 0; i &lt; 10; i++) +{ + glm::mat4 model = glm::mat4(1.0f); + model = <function id='55'>glm::translate</function>(model, cubePositions[i]); + float angle = 20.0f * i; + model = <function id='57'>glm::rotate</function>(model, <function id='63'>glm::radians</function>(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 36); +} +</code></pre> + +<p> + Also, don't forget to actually specify the direction of the light source (note that we define the direction as a direction <strong>from</strong> the light source; you can quickly see the light's direction is pointing downwards): +</p> + +<pre><code> +lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f); +</code></pre> + +<note> + <p> + We've been passing the light's position and direction vectors as <code>vec3</code>s for a while now, but some people tend to prefer to keep all the vectors defined as <code>vec4</code>. When defining position vectors as a <code>vec4</code> it is important to set the <code>w</code> component to <code>1.0</code> so translation and projections are properly applied. However, when defining a direction vector as a <code>vec4</code> we don't want translations to have an effect (since they just represent directions, nothing more) so then we define the <code>w</code> component to be <code>0.0</code>. + </p> + + <p> + Direction vectors can then be represented as: <code>vec4(-0.2f, -1.0f, -0.3f, 0.0f)</code>. This can also function as an easy check for light types: you could check if the <code>w</code> component is equal to <code>1.0</code> to see that we now have a light's position vector and if <code>w</code> is equal to <code>0.0</code> we have a light's direction vector; so adjust the calculations based on that: + </p> + +<pre><code> +if(lightVector.w == 0.0) // note: be careful for floating point errors + // do directional light calculations +else if(lightVector.w == 1.0) + // do light calculations using the light's position (as in previous chapters) +</code></pre> + + <p> + Fun fact: this is actually how the old OpenGL (fixed-functionality) determined if a light source was a directional light or a positional light source and adjusted its lighting based on that. + </p> +</note> + +<p> + If you'd now compile the application and fly through the scene it looks like there is a sun-like light source casting light on all the objects. Can you see that the diffuse and specular components all react as if there was a light source somewhere in the sky? It'll look something like this: +</p> + + <img src="/img/lighting/light_casters_directional_light.png" class="clean"/> + +<p> + You can find the full source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/5.1.light_casters_directional/light_casters_directional.cpp" target="_blank">here</a>. +</p> + +<h1>Point lights</h1> +<p> + Directional lights are great for global lights that illuminate the entire scene, but we usually also want several <def>point lights</def> scattered throughout the scene. A point light is a light source with a given position somewhere in a world that illuminates in all directions, where the light rays fade out over distance. Think of light bulbs and torches as light casters that act as a point light. +</p> + + <img src="/img/lighting/light_casters_point.png" class="clean"/> + + <p> + In the earlier chapters we've been working with a simplistic point light. We had a light source at a given position that scatters light in all directions from that given light position. However, the light source we defined simulated light rays that never fade out thus making it look like the light source is extremely strong. In most 3D applications we'd like to simulate a light source that only illuminates an area close to the light source and not the entire scene. +</p> + + <p> + If you'd add the 10 containers to the lighting scene from the previous chapters, you'd notice that the container all the way in the back is lit with the same intensity as the container in front of the light; there is no logic yet that diminishes light over distance. We want the container in the back to only be slightly lit in comparison to the containers close to the light source. + </p> + +<h2>Attenuation</h2> +<p> + To reduce the intensity of light over the distance a light ray travels is generally called <def>attenuation</def>. One way to reduce the light intensity over distance is to simply use a linear equation. Such an equation would linearly reduce the light intensity over the distance thus making sure that objects at a distance are less bright. However, such a linear function tends to look a bit fake. In the real world, lights are generally quite bright standing close by, but the brightness of a light source diminishes quickly at a distance; the remaining light intensity then slowly diminishes over distance. We are thus in need of a different equation for reducing the light's intensity. +</p> + + <p> + Luckily some smart people already figured this out for us. The following formula calculates an attenuation value based on a fragment's distance to the light source which we later multiply with the light's intensity vector: + </p> + + \begin{equation} F_{att} = \frac{1.0}{K_c + K_l * d + K_q * d^2} \end{equation} + + <p> + Here \(d\) represents the distance from the fragment to the light source. Then to calculate the attenuation value we define 3 (configurable) terms: a <def>constant</def> term \(K_c\), a <def>linear</def> term \(K_l\) and a <def>quadratic</def> term \(K_q\). + <ul> + <li>The constant term is usually kept at <code>1.0</code> which is mainly there to make sure the denominator never gets smaller than <code>1</code> since it would otherwise boost the intensity with certain distances, which is not the effect we're looking for.</li> + <li>The linear term is multiplied with the distance value that reduces the intensity in a linear fashion.</li> + <li>The quadratic term is multiplied with the quadrant of the distance and sets a quadratic decrease of intensity for the light source. The quadratic term will be less significant compared to the linear term when the distance is small, but gets much larger as the distance grows. </li> + </ul> + Due to the quadratic term the light will diminish mostly at a linear fashion until the distance becomes large enough for the quadratic term to surpass the linear term and then the light intensity will decrease a lot faster. The resulting effect is that the light is quite intense when at a close range, but quickly loses its brightness over distance until it eventually loses its brightness at a more slower pace. The following graph shows the effect such an attenuation has over a distance of <code>100</code>: + </p> + + <img src="/img/lighting/attenuation.png" class="clean"/> + + <p> + You can see that the light has the highest intensity when the distance is small, but as soon as the distance grows its intensity is significantly reduced and slowly reaches <code>0</code> intensity at around a distance of <code>100</code>. This is exactly what we want. + </p> + + <h3>Choosing the right values</h3> + <p> + But at what values do we set those 3 terms? Setting the right values depend on many factors: the environment, the distance you want a light to cover, the type of light etc. In most cases, it simply is a question of experience and a moderate amount of tweaking. The following table shows some of the values these terms could take to simulate a realistic (sort of) light source that covers a specific radius (distance). The first column specifies the distance a light will cover with the given terms. These values are good starting points for most lights, with courtesy of <a href="http://www.ogre3d.org/tikiwiki/tiki-index.php?page=-Point+Light+Attenuation" target="_blank">Ogre3D's wiki</a>: + </p> + +<table> + <tr> + <th>Distance</th> + <th>Constant</th> + <th>Linear</th> + <th>Quadratic</th> + </tr> + <tr> + <td><code>7</code></td> + <td><code>1.0</code></td> + <td><code>0.7</code></td> + <td><code>1.8</code></td> + </tr> + <tr> + <td><code>13</code></td> + <td><code>1.0</code></td> + <td><code>0.35</code></td> + <td><code>0.44</code></td> + </tr> + <tr> + <td><code>20</code></td> + <td><code>1.0</code></td> + <td><code>0.22</code></td> + <td><code>0.20</code></td> + </tr> + <tr> + <td><code>32</code></td> + <td><code>1.0</code></td> + <td><code>0.14</code></td> + <td><code>0.07</code></td> + </tr><tr> + <td><code>50</code></td> + <td><code>1.0</code></td> + <td><code>0.09</code></td> + <td><code>0.032</code></td> + </tr> + <tr> + <td><code>65</code></td> + <td><code>1.0</code></td> + <td><code>0.07</code></td> + <td><code>0.017</code></td> + </tr><tr> + <td><code>100</code></td> + <td><code>1.0</code></td> + <td><code>0.045</code></td> + <td><code>0.0075</code></td> + </tr><tr> + <td><code>160</code></td> + <td><code>1.0</code></td> + <td><code>0.027</code></td> + <td><code>0.0028</code></td> + </tr> + <tr> + <td><code>200</code></td> + <td><code>1.0</code></td> + <td><code>0.022</code></td> + <td><code>0.0019</code></td> + </tr><tr> + <td><code>325</code></td> + <td><code>1.0</code></td> + <td><code>0.014</code></td> + <td><code>0.0007</code></td> + </tr> + <tr> + <td><code>600</code></td> + <td><code>1.0</code></td> + <td><code>0.007</code></td> + <td><code>0.0002</code></td> + </tr> + <tr> + <td><code>3250</code></td> + <td><code>1.0</code></td> + <td><code>0.0014</code></td> + <td><code>0.000007</code></td> + </tr> +</table> + +<p> + As you can see, the constant term \(K_c\) is kept at <code>1.0</code> in all cases. The linear term \(K_l\) is usually quite small to cover larger distances and the quadratic term \(K_q\) is even smaller. Try to experiment a bit with these values to see their effect in your implementation. In our environment a distance of <code>32</code> to <code>100</code> is generally enough for most lights. +</p> + +<h3>Implementing attenuation</h3> +<p> + To implement attenuation we'll be needing 3 extra values in the fragment shader: namely the constant, linear and quadratic terms of the equation. These are best stored in the <fun>Light</fun> struct we defined earlier. Note that we need to calculate <var>lightDir</var> again using <var>position</var> as this is a point light (as we did in the previous chapter) and not a directional light. +</p> + +<pre><code> +struct Light { + vec3 position; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + float constant; + float linear; + float quadratic; +}; +</code></pre> + +<p> + Then we set the terms in our application: we want the light to cover a distance of <code>50</code> so we'll use the appropriate constant, linear and quadratic terms from the table: +</p> + +<pre><code> +lightingShader.setFloat("light.constant", 1.0f); +lightingShader.setFloat("light.linear", 0.09f); +lightingShader.setFloat("light.quadratic", 0.032f); +</code></pre> + + <p> + Implementing attenuation in the fragment shader is relatively straightforward: we simply calculate an attenuation value based on the equation and multiply this with the ambient, diffuse and specular components. + </p> + +<p> + We do need the distance to the light source for the equation to work though. Remember how we can calculate the length of a vector? We can retrieve the distance term by calculating the difference vector between the fragment and the light source and take that resulting vector's length. We can use GLSL's built-in <fun>length</fun> function for that purpose: + </p> + +<pre><code> +float distance = length(light.position - FragPos); +float attenuation = 1.0 / (light.constant + light.linear * distance + + light.quadratic * (distance * distance)); +</code></pre> + +<p> + Then we include this attenuation value in the lighting calculations by multiplying the attenuation value with the ambient, diffuse and specular colors. + </p> + + <note> + We could leave the ambient component alone so ambient lighting is not decreased over distance, but if we were to use more than 1 light source all the ambient components will start to stack up. In that case we want to attenuate ambient lighting as well. Simply play around with what's best for your environment. + </note> + +<pre><code> +ambient *= attenuation; +diffuse *= attenuation; +specular *= attenuation; +</code></pre> + +<p> + If you'd run the application you'd get something like this: +</p> + + <img src="/img/lighting/light_casters_point_light.png" class="clean"/> + + <p> + You can see that right now only the front containers are lit with the closest container being the brightest. The containers in the back are not lit at all since they're too far from the light source. You can find the source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/5.2.light_casters_point/light_casters_point.cpp" target="_blank">here</a>. + </p> + + <p> + A point light is thus a light source with a configurable location and attenuation applied to its lighting calculations. Yet another type of light for our lighting arsenal. + </p> + +<h1>Spotlight</h1> +<p> + The last type of light we're going to discuss is a <def>spotlight</def>. A spotlight is a light source that is located somewhere in the environment that, instead of shooting light rays in all directions, only shoots them in a specific direction. The result is that only the objects within a certain radius of the spotlight's direction are lit and everything else stays dark. A good example of a spotlight would be a street lamp or a flashlight. +</p> + +<p> + A spotlight in OpenGL is represented by a world-space position, a direction and a <def>cutoff</def> angle that specifies the radius of the spotlight. For each fragment we calculate if the fragment is between the spotlight's cutoff directions (thus in its cone) and if so, we lit the fragment accordingly. The following image gives you an idea of how a spotlight works: +</p> + + <img src="/img/lighting/light_casters_spotlight_angles.png" class="clean"/> + +<p> + <ul> + <li><code>LightDir</code>: the vector pointing from the fragment to the light source.</li> + <li><code>SpotDir</code>: the direction the spotlight is aiming at.</li> + <li><code>Phi</code> \(\phi\): the cutoff angle that specifies the spotlight's radius. Everything outside this angle is not lit by the spotlight.</li> + <li><code>Theta</code> \(\theta\): the angle between the <var>LightDir</var> vector and the <var>SpotDir</var> vector. The \(\theta\) value should be smaller than \(\Phi\) to be inside the spotlight. </li> + </ul> +</p> + +<p> + So what we basically need to do, is calculate the dot product (returns the cosine of the angle between two unit vectors) between the <var>LightDir</var> vector and the <var>SpotDir</var> vector and compare this with the cutoff angle \(\phi\). Now that you (sort of) understand what a spotlight is all about we're going to create one in the form of a flashlight. +</p> + +<h2>Flashlight</h2> +<p> + A flashlight is a spotlight located at the viewer's position and usually aimed straight ahead from the player's perspective. A flashlight is basically a normal spotlight, but with its position and direction continually updated based on the player's position and orientation. +</p> + +<p> + So, the values we're going to need for the fragment shader are the spotlight's position vector (to calculate the fragment-to-light's direction vector), the spotlight's direction vector and the cutoff angle. We can store these values in the <fun>Light</fun> struct: +</p> + +<pre><code> +struct Light { + vec3 position; + vec3 direction; + float cutOff; + ... +}; +</code></pre> + +<p> + Next we pass the appropriate values to the shader: +</p> + +<pre><code> +lightingShader.setVec3("light.position", camera.Position); +lightingShader.setVec3("light.direction", camera.Front); +lightingShader.setFloat("light.cutOff", glm::cos(<function id='63'>glm::radians</function>(12.5f))); +</code></pre> + +<p> + As you can see we're not setting an angle for the cutoff value but calculate the cosine value based on an angle and pass the cosine result to the fragment shader. The reason for this is that in the fragment shader we're calculating the dot product between the <code>LightDir</code> and the <code>SpotDir</code> vector and the dot product returns a cosine value and not an angle; and we can't directly compare an angle with a cosine value. To get the angle in the shader we then have to calculate the inverse cosine of the dot product's result which is an expensive operation. So to save some performance we calculate the cosine value of a given cutoff angle beforehand and pass this result to the fragment shader. Since both angles are now represented as cosines, we can directly compare between them without expensive operations. +</p> + +<p> + Now what's left to do is calculate the theta \(\theta\) value and compare this with the cutoff \(\phi\) value to determine if we're in or outside the spotlight: +</p> + +<pre><code> +float theta = dot(lightDir, normalize(-light.direction)); + +if(theta > light.cutOff) +{ + // do lighting calculations +} +else // else, use ambient light so scene isn't completely dark outside the spotlight. + color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0); +</code></pre> + +<p> + We first calculate the dot product between the <var>lightDir</var> vector and the negated <var>direction</var> vector (negated, because we want the vectors to point towards the light source, instead of from). Be sure to normalize all the relevant vectors. +</p> + +<note> + <p> + You may be wondering why there is a <code>&gt;</code> sign instead of a <code>&lt;</code> sign in the <code>if</code> guard. Shouldn't <var>theta</var> be smaller than the light's cutoff value to be inside the spotlight? That is right, but don't forget angle values are represented as cosine values and an angle of <code>0</code> degrees is represented as the cosine value of <code>1.0</code> while an angle of <code>90</code> degrees is represented as the cosine value of <code>0.0</code> as you can see here: + </p> + + <img src="/img/lighting/light_casters_cos.png"/> + + <p> + You can now see that the closer the cosine value is to <code>1.0</code> the smaller its angle. Now it makes sense why <var>theta</var> needs to be larger than the cutoff value. The cutoff value is currently set at the cosine of <code>12.5</code> which is equal to <code>0.976</code> so a cosine <var>theta</var> value between <code>0.976</code> and <code>1.0</code> would result in the fragment being lit as if inside the spotlight. + </p> +</note> + + <p> + Running the application results in a spotlight that only lights the fragments that are directly inside the cone of the spotlight. It'll look something like this: + </p> + + <img src="/img/lighting/light_casters_spotlight_hard.png" class="clean"/> + + <p> + You can find the full source code <a href="/code_viewer_gh.php?code=src/2.lighting/5.3.light_casters_spot/light_casters_spot.cpp" target="_blank">here</a>. + </p> + + <p> + It still looks a bit fake though, mostly because the spotlight has hard edges. Wherever a fragment reaches the edge of the spotlight's cone it is shut down completely instead of with a nice smooth fade. A realistic spotlight would reduce the light gradually around its edges. + </p> + +<h2>Smooth/Soft edges</h2> +<p> + To create the effect of a smoothly-edged spotlight we want to simulate a spotlight having an <def>inner</def> and an <def>outer</def> cone. We can set the inner cone as the cone defined in the previous section, but we also want an outer cone that gradually dims the light from the inner to the edges of the outer cone. +</p> + +<p> + To create an outer cone we simply define another cosine value that represents the angle between the spotlight's direction vector and the outer cone's vector (equal to its radius). Then, if a fragment is between the inner and the outer cone it should calculate an intensity value between <code>0.0</code> and <code>1.0</code>. If the fragment is inside the inner cone its intensity is equal to <code>1.0</code> and <code>0.0</code> if the fragment is outside the outer cone. +</p> + +<p> + We can calculate such a value using the following equation: + + \begin{equation} I = \frac{\theta - \gamma}{\epsilon} \end{equation} + + Here \(\epsilon\) (epsilon) is the cosine difference between the inner (\(\phi\)) and the outer cone (\(\gamma\)) (\(\epsilon = \phi - \gamma\)). The resulting \(I\) value is then the intensity of the spotlight at the current fragment. + </p> + + <p> + It is a bit hard to visualize how this formula actually works so let's try it out with a few sample values: +</p> + + +<table> + <tr> + <th>\(\theta\)</th> + <th>\(\theta\) in degrees</th> + <th>\(\phi\) (inner cutoff)</th> + <th>\(\phi\) in degrees</th> + <th>\(\gamma\) (outer cutoff)</th> + <th>\(\gamma\) in degrees</th> + <th>\(\epsilon\)</th> + <th>\(I\)</th> + </tr> + <tr> + <td><code>0.87</code></td> + <td><code>30</code></td> + <td><code>0.91</code></td> + <td><code>25</code></td> + <td><code>0.82</code></td> + <td><code>35</code></td> + <td><code>0.91 - 0.82 = 0.09</code></td> + <td><code>0.87 - 0.82 / 0.09 = 0.56</code></td> + </tr> + <tr> + <td><code>0.9</code></td> + <td><code>26</code></td> + <td><code>0.91</code></td> + <td><code>25</code></td> + <td><code>0.82</code></td> + <td><code>35</code></td> + <td><code>0.91 - 0.82 = 0.09</code></td> + <td><code>0.9 - 0.82 / 0.09 = 0.89</code></td> + </tr> + <tr> + <td><code>0.97</code></td> + <td><code>14</code></td> + <td><code>0.91</code></td> + <td><code>25</code></td> + <td><code>0.82</code></td> + <td><code>35</code></td> + <td><code>0.91 - 0.82 = 0.09</code></td> + <td><code>0.97 - 0.82 / 0.09 = 1.67</code></td> + </tr> + <tr> + <td><code>0.83</code></td> + <td><code>34</code></td> + <td><code>0.91</code></td> + <td><code>25</code></td> + <td><code>0.82</code></td> + <td><code>35</code></td> + <td><code>0.91 - 0.82 = 0.09</code></td> + <td><code>0.83 - 0.82 / 0.09 = 0.11</code></td> + </tr> + <tr> + <td><code>0.64</code></td> + <td><code>50</code></td> + <td><code>0.91</code></td> + <td><code>25</code></td> + <td><code>0.82</code></td> + <td><code>35</code></td> + <td><code>0.91 - 0.82 = 0.09</code></td> + <td><code>0.64 - 0.82 / 0.09 = -2.0</code></td> + </tr> + <tr> + <td><code>0.966</code></td> + <td><code>15</code></td> + <td><code>0.9978</code></td> + <td><code>12.5</code></td> + <td><code>0.953</code></td> + <td><code>17.5</code></td> + <td><code>0.9978 - 0.953 = 0.0448</code></td> + <td><code>0.966 - 0.953 / 0.0448 = 0.29</code></td> + </tr> +</table> + +<p> + As you can see we're basically interpolating between the outer cosine and the inner cosine based on the \(\theta\) value. If you still don't really see what's going on, don't worry, you can simply take the formula for granted and return here when you're much older and wiser. +</p> + +<p> + We now have an intensity value that is either negative when outside the spotlight, higher than <code>1.0</code> when inside the inner cone, and somewhere in between around the edges. If we properly clamp the values we don't need an <code>if-else</code> in the fragment shader anymore and we can simply multiply the light components with the calculated intensity value: +</p> + +<pre><code> +float theta = dot(lightDir, normalize(-light.direction)); +float epsilon = light.cutOff - light.outerCutOff; +float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); +... +// we'll leave ambient unaffected so we always have a little light. +diffuse *= intensity; +specular *= intensity; +... +</code></pre> + +<p> + Note that we use the <fun>clamp</fun> function that <def>clamps</def> its first argument between the values <code>0.0</code> and <code>1.0</code>. This makes sure the intensity values won't end up outside the [<code>0</code>, <code>1</code>] range. +</p> + +<p> + Make sure you add the <var>outerCutOff</var> value to the <fun>Light</fun> struct and set its uniform value in the application. For the following image an inner cutoff angle of <code>12.5</code> and an outer cutoff angle of <code>17.5</code> was used: +</p> + + <img src="/img/lighting/light_casters_spotlight.png" class="clean"/> + + <p> + Ahhh, that's much better. Play around with the inner and outer cutoff angles and try to create a spotlight that better suits your needs. You can find the source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/5.4.light_casters_spot_soft/light_casters_spot_soft.cpp" target="_blank">here</a>. + </p> + +<p> + Such a flashlight/spotlight type of lamp is perfect for horror games and combined with directional and point lights the environment will really start to light up. +</p> + +<h2>Exercises</h2> + <ul> + <li>Try experimenting with all the different light types and their fragment shaders. Try inverting some vectors and/or use &lt; instead of &gt;. Try to explain the different visual outcomes.</li> + </ul> + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Lighting-maps.html b/pub/Lighting/Lighting-maps.html @@ -0,0 +1,532 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Lighting maps</h1> +<h1 id="content-url" style='display:none;'>Lighting/Lighting-maps</h1> +<p> + In the <a href="https://learnopengl.com/Lighting/Materials" target="_blank">previous</a> chapter we discussed the possibility of each object having a unique material of its own that reacts differently to light. This is great for giving each object a unique look in comparison to other objects, but still doesn't offer much flexibility on the visual output of an object. +</p> + +<p> + In the previous chapter we defined a material for an entire object as a whole. Objects in the real world however usually do not consist of a single material, but of several materials. Think of a car: its exterior consists of a shiny fabric, it has windows that partly reflect the surrounding environment, its tires are all but shiny so they don't have specular highlights and it has rims that are super shiny (if you actually washed your car alright). The car also has diffuse and ambient colors that are not the same for the entire object; a car displays many different ambient/diffuse colors. All by all, such an object has different material properties for each of its different parts. +</p> + +<p> + So the material system in the previous chapter isn't sufficient for all but the simplest models so we need to extend the system by introducing <em>diffuse</em> and <em>specular</em> maps. These allow us to influence the diffuse (and indirectly the ambient component since they should be the same anyways) and the specular component of an object with much more precision. +</p> + +<h1>Diffuse maps</h1> +<p> + What we want is some way to set the diffuse colors of an object for each individual fragment. Some sort of system where we can retrieve a color value based on the fragment's position on the object? +</p> + +<p> + This should probably all sound familiar and we've been using such a system for a while now. This sounds just like <em>textures</em> we've extensively discussed in one of the <a href="https://learnopengl.com/Getting-started/Textures" target="_blank">earlier</a> chapters and it basically is just that: a texture. We're just using a different name for the same underlying principle: using an image wrapped around an object that we can index for unique color values per fragment. In lit scenes this is usually called a <def>diffuse map</def> (this is generally how 3D artists call them before PBR) since a texture image represents all of the object's diffuse colors. +</p> + +<p> + To demonstrate diffuse maps we're going to use the <a href="/img/textures/container2.png" target="_blank">following image</a> of a wooden container with a steel border: +</p> + +<img src="/img/textures/container2.png" style="width:300px; height:300px"/> + +<p> + Using a diffuse map in shaders is exactly like we showed in the texture chapter. This time however we store the texture as a <code>sampler2D</code> inside the <fun>Material</fun> struct. We replace the earlier defined <code>vec3</code> diffuse color vector with the diffuse map. +</p> + +<warning>Keep in mind that <code>sampler2D</code> is a so called <def>opaque type</def> which means we can't instantiate these types, but only define them as uniforms. If the struct would be instantiated other than as a uniform (like a function parameter) GLSL could throw strange errors; the same thus applies to any struct holding such opaque types. +</warning> + +<p> + We also remove the ambient material color vector since the ambient color is equal to the diffuse color anyways now that we control ambient with the light. So there's no need to store it separately: +</p> + +<pre><code> +struct Material { + sampler2D diffuse; + vec3 specular; + float shininess; +}; +... +in vec2 TexCoords; +</code></pre> + +<note> + If you're a bit stubborn and still want to set the ambient colors to a different value (other than the diffuse value) you can keep the ambient <code>vec3</code>, but then the ambient colors would still remain the same for the entire object. To get different ambient values for each fragment you'd have to use another texture for ambient values alone. +</note> + +<p> + Note that we are going to need texture coordinates again in the fragment shader, so we declared an extra input variable. Then we simply sample from the texture to retrieve the fragment's diffuse color value: +</p> + +<pre><code> +vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); +</code></pre> + +<p> + Also, don't forget to set the ambient material's color equal to the diffuse material's color as well: +</p> + +<pre><code> +vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); +</code></pre> + +<p> + And that's all it takes to use a diffuse map. As you can see it is nothing new, but it does provide a dramatic increase in visual quality. To get it working we do need to update the vertex data with texture coordinates, transfer them as vertex attributes to the fragment shader, load the texture, and bind the texture to the appropriate texture unit. +</p> + +<p> + The updated vertex data can be found <a href="/code_viewer.php?code=lighting/vertex_data_textures" target="_blank">here</a>. The vertex data now includes vertex positions, normal vectors, and texture coordinates for each of the cube's vertices. Let's update the vertex shader to accept texture coordinates as a vertex attribute and forward them to the fragment shader: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; +... +out vec2 TexCoords; + +void main() +{ + ... + TexCoords = aTexCoords; +} +</code></pre> + +<p> + Be sure to update the vertex attribute pointers of both VAOs to match the new vertex data and load the container image as a texture. Before rendering the cube we want to assign the right texture unit to the <var>material.diffuse</var> uniform sampler and bind the container texture to this texture unit: +</p> + +<pre><code> +lightingShader.setInt("material.diffuse", 0); +... +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, diffuseMap); +</code></pre> + +<p> + Now using a diffuse map we get an enormous boost in detail again and this time the container really starts to shine (quite literally). Your container now probably looks something like this: +</p> + +<img src="/img/lighting/materials_diffuse_map.png" class="clean"/> + +<p> + You can find the full source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/4.1.lighting_maps_diffuse_map/lighting_maps_diffuse.cpp" target="_blank">here</a>. +</p> + +<h1>Specular maps</h1> +<p> + You probably noticed that the specular highlight looks a bit odd since the object is a container that mostly consists of wood and wood doesn't have specular highlights like that. We can fix this by setting the specular material of the object to <code>vec3(0.0)</code> but that would mean that the steel borders of the container would stop showing specular highlights as well and steel <strong>should</strong> show specular highlights. We would like to control what parts of the object should show a specular highlight with varying intensity. This is a problem that sounds familiar. Coincidence? I think not. +</p> + +<p> + We can also use a texture map just for specular highlights. This means we need to generate a black and white (or colors if you feel like it) texture that defines the specular intensities of each part of the object. An example of a <a href="/img/textures/container2_specular.png" target="_blank">specular map</a> is the following image: +</p> + +<img src="/img/textures/container2_specular.png" style="width:300px; height:300px"/> + +<p> + The intensity of the specular highlight comes from the brightness of each pixel in the image. Each pixel of the specular map can be displayed as a color vector where black represents the color vector <code>vec3(0.0)</code> and gray the color vector <code>vec3(0.5)</code> for example. In the fragment shader we then sample the corresponding color value and multiply this value with the light's specular intensity. The more 'white' a pixel is, the higher the result of the multiplication and thus the brighter the specular component of an object becomes. +</p> + +<p> + Because the container mostly consists of wood, and wood as a material should have no specular highlights, the entire wooden section of the diffuse texture was converted to black: black sections do not have any specular highlight. The steel border of the container has varying specular intensities with the steel itself being relatively susceptible to specular highlights while the cracks are not. +</p> + +<note> + Technically wood also has specular highlights although with a much lower shininess value (more light scattering) and less impact, but for learning purposes we can just pretend wood doesn't have any reaction to specular light. +</note> + +<p> +Using tools like <em>Photoshop</em> or <em>Gimp</em> it is relatively easy to transform a diffuse texture to a specular image like this by cutting out some parts, transforming it to black and white and increasing the brightness/contrast. +</p> + +<h2>Sampling specular maps</h2> +<p> + A specular map is just like any other texture so the code is similar to the diffuse map code. Make sure to properly load the image and generate a texture object. Since we're using another texture sampler in the same fragment shader we have to use a different texture unit (see <a href="https://learnopengl.com/Getting-started/Textures" target="_blank">Textures</a>) for the specular map so let's bind it to the appropriate texture unit before rendering: +</p> + +<pre><code> +lightingShader.setInt("material.specular", 1); +... +<function id='49'>glActiveTexture</function>(GL_TEXTURE1); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, specularMap); +</code></pre> + +<p> + Then update the material properties of the fragment shader to accept a <code>sampler2D</code> as its specular component instead of a <code>vec3</code>: +</p> + +<pre><code> +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; +</code></pre> + +<p> + And lastly we want to sample the specular map to retrieve the fragment's corresponding specular intensity: +</p> + +<pre><code> +vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); +vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); +vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); +FragColor = vec4(ambient + diffuse + specular, 1.0); +</code></pre> + +<p> + By using a specular map we can specify with enormous detail what parts of an object have <em>shiny</em> properties and we can even control the corresponding intensity. Specular maps give us an added layer of control over lighting on top of the diffuse map. +</p> + +<note> + If you don't want to be too mainstream you could also use actual colors in the specular map to not only set the specular intensity of each fragment, but also the color of the specular highlight. Realistically however, the color of the specular highlight is mostly determined by the light source itself so it wouldn't generate realistic visuals (that's why the images are usually black and white: we only care about the intensity). +</note> + +<p> + If you would now run the application you can clearly see that the container's material now closely resembles that of an actual wooden container with steel frames: +</p> + +<img src="/img/lighting/materials_specular_map.png" class="clean"/> + +<p> + You can find the full source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/4.2.lighting_maps_specular_map/lighting_maps_specular.cpp" target="_blank">here</a>. +</p> + +<p> + Using diffuse and specular maps we can really add an enormous amount of detail into relatively simple objects. We can even add more detail into the objects using other texture maps like <def>normal/bump maps</def> and/or <def>reflection maps</def>, but that is something we'll reserve for later chapters. Show your container to all your friends and family and be content with the fact that our container can one day become even prettier than it already is! +</p> + +<h2>Exercises</h2> +<p> + <ul> + <li>Fool around with the light source's ambient, diffuse and specular vectors and see how they affect the visual output of the container.</li> + <li>Try inverting the color values of the specular map in the fragment shader so that the wood shows specular highlights and the steel borders do not (note that due to the cracks in the steel border the borders still show some specular highlight, although with less intensity): <a href="/code_viewer_gh.php?code=src/2.lighting/4.3.lighting_maps_exercise2/lighting_maps_exercise2.cpp" target="_blank">solution</a>.</li> + <li>Try creating a specular map from the diffuse texture that uses actual colors instead of black and white and see that the result doesn't look too realistic. You can use this <a href="/img/lighting/lighting_maps_specular_color.png" target="_blank">colored specular map</a> if you can't generate one yourself: <a href="/img/lighting/lighting_maps_exercise3.png" target="_blank">result</a>.</li> + <li> + Also add something they call an <def>emission map</def> which is a texture that stores emission values per fragment. Emission values are colors an object may <em>emit</em> as if it contains a light source itself; this way an object can glow regardless of the light conditions. Emission maps are often what you see when objects in a game glow (like <a href="/img/lighting/lighting_maps_eyes_robot.jpg" target="_blank">eyes of a robot</a>, or <a href="/img/lighting/lighting_maps_strips_container.png" target="_blank">light strips on a container</a>). Add the <a href="/img/textures/matrix.jpg" target="_blank">following</a> texture (by creativesam) as an emission map onto the container as if the letters emit light: <a href="/code_viewer_gh.php?code=src/2.lighting/4.4.lighting_maps_exercise4/lighting_maps_exercise4.cpp" target="_blank">solution</a>; <a href="/img/lighting/lighting_maps_exercise4.png" target="_blank">result</a>.</li> + </ul> +</p> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Materials.html b/pub/Lighting/Materials.html @@ -0,0 +1,541 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Materials</h1> +<h1 id="content-url" style='display:none;'>Lighting/Materials</h1> +<p> + In the real world, each object has a different reaction to light. Steel objects are often shinier than a clay vase for example and a wooden container doesn't react the same to light as a steel container. Some objects reflect the light without much scattering resulting in small specular highlights and others scatter a lot giving the highlight a larger radius. If we want to simulate several types of objects in OpenGL we have to define <def>material</def> properties specific to each surface. +</p> + +<p> + In the previous chapter we defined an object and light color to define the visual output of the object, combined with an ambient and specular intensity component. When describing a surface we can define a material color for each of the 3 lighting components: ambient, diffuse and specular lighting. By specifying a color for each of the components we have fine-grained control over the color output of the surface. Now add a shininess component to those 3 colors and we have all the material properties we need: +</p> + +<pre><code> +#version 330 core +struct Material { + vec3 ambient; + vec3 diffuse; + vec3 specular; + float shininess; +}; + +uniform Material material; +</code></pre> + +<p> + In the fragment shader we create a <code>struct</code> to store the material properties of the surface. We can also store them as individual uniform values, but storing them as a struct keeps it more organized. We first define the layout of the struct and then simply declare a uniform variable with the newly created struct as its type. +</p> + +<p> + As you can see, we define a color vector for each of the Phong lighting's components. The <var>ambient</var> material vector defines what color the surface reflects under ambient lighting; this is usually the same as the surface's color. The <var>diffuse</var> material vector defines the color of the surface under diffuse lighting. The diffuse color is (just like ambient lighting) set to the desired surface's color. The <var>specular</var> material vector sets the color of the specular highlight on the surface (or possibly even reflect a surface-specific color). Lastly, the <var>shininess</var> impacts the scattering/radius of the specular highlight. +</p> + +<p> + With these 4 components that define an object's material we can simulate many real-world materials. A table as found at <a href="http://devernay.free.fr/cours/opengl/materials.html" target="_blank">devernay.free.fr</a> shows a list of material properties that simulate real materials found in the outside world. The following image shows the effect several of these real world material values have on our cube: +</p> + +<img src="/img/lighting/materials_real_world.png"/> + +<p> + As you can see, by correctly specifying the material properties of a surface it seems to change the perception we have of the object. The effects are clearly noticeable, but for the more realistic results we'll need to replace the cube with something more complicated. In the <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">Model Loading</a> chapters we'll discuss more complicated shapes. +</p> + +<p> + Figuring out the right material settings for an object is a difficult feat that mostly requires experimentation and a lot of experience. It's not that uncommon to completely destroy the visual quality of an object by a misplaced material. +</p> + +<p> + Let's try implementing such a material system in the shaders. +</p> + +<h1>Setting materials</h1> +<p> + We created a uniform material struct in the fragment shader so next we want to change the lighting calculations to comply with the new material properties. Since all the material variables are stored in a struct we can access them from the <var>material</var> uniform: +</p> + +<pre><code> +void main() +{ + // ambient + vec3 ambient = lightColor * material.ambient; + + // diffuse + vec3 norm = normalize(Normal); + vec3 lightDir = normalize(lightPos - FragPos); + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = lightColor * (diff * material.diffuse); + + // specular + vec3 viewDir = normalize(viewPos - FragPos); + vec3 reflectDir = reflect(-lightDir, norm); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + vec3 specular = lightColor * (spec * material.specular); + + vec3 result = ambient + diffuse + specular; + FragColor = vec4(result, 1.0); +} +</code></pre> + +<p> + As you can see we now access all of the material struct's properties wherever we need them and this time calculate the resulting output color with the help of the material's colors. Each of the object's material attributes are multiplied with their respective lighting components. +</p> + +<p> + We can set the material of the object in the application by setting the appropriate uniforms. A struct in GLSL however is not special in any regard when setting uniforms; a struct only really acts as a namespace of uniform variables. If we want to fill the struct we will have to set the individual uniforms, but prefixed with the struct's name: +</p> + +<pre><code> +lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f); +lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f); +lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f); +lightingShader.setFloat("material.shininess", 32.0f); +</code></pre> + +<p> + We set the ambient and diffuse component to the color we'd like the object to have and set the specular component of the object to a medium-bright color; we don't want the specular component to be too strong. We also keep the shininess at <code>32</code>. +</p> + +<p> + We can now easily influence the object's material from the application. Running the program gives you something like this: +</p> + +<img src="/img/lighting/materials_with_material.png" class="clean"/> + +<p> + It doesn't really look right though? +</p> + +<h2>Light properties</h2> +<p> + The object is way too bright. The reason for the object being too bright is that the ambient, diffuse and specular colors are reflected with full force from any light source. Light sources also have different intensities for their ambient, diffuse and specular components respectively. In the previous chapter we solved this by varying the ambient and specular intensities with a strength value. We want to do something similar, but this time by specifying intensity vectors for each of the lighting components. If we'd visualize <var>lightColor</var> as <code>vec3(1.0)</code> the code would look like this: +</p> + +<pre><code> +vec3 ambient = vec3(1.0) * material.ambient; +vec3 diffuse = vec3(1.0) * (diff * material.diffuse); +vec3 specular = vec3(1.0) * (spec * material.specular); +</code></pre> + +<p> + So each material property of the object is returned with full intensity for each of the light's components. These <code>vec3(1.0)</code> values can be influenced individually as well for each light source and this is usually what we want. Right now the ambient component of the object is fully influencing the color of the cube. The ambient component shouldn't really have such a big impact on the final color so we can restrict the ambient color by setting the light's ambient intensity to a lower value: +</p> + +<pre><code> +vec3 ambient = vec3(0.1) * material.ambient; +</code></pre> + +<p> + We can influence the diffuse and specular intensity of the light source in the same way. This is closely similar to what we did in the <a href="https://learnopengl.com/Lighting/Basic-Lighting" target="_blank">previous</a> chapter; you could say we already created some light properties to influence each lighting component individually. We'll want to create something similar to the material struct for the light properties: +</p> + +<pre><code> +struct Light { + vec3 position; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +uniform Light light; +</code></pre> + +<p> + A light source has a different intensity for its <var>ambient</var>, <var>diffuse</var> and <var>specular</var> components. The ambient light is usually set to a low intensity because we don't want the ambient color to be too dominant. The diffuse component of a light source is usually set to the exact color we'd like a light to have; often a bright white color. The specular component is usually kept at <code>vec3(1.0)</code> shining at full intensity. Note that we also added the light's position vector to the struct. + +<p> + Just like with the material uniform we need to update the fragment shader: +</p> + +<pre><code> +vec3 ambient = light.ambient * material.ambient; +vec3 diffuse = light.diffuse * (diff * material.diffuse); +vec3 specular = light.specular * (spec * material.specular); +</code></pre> + +<p> + We then want to set the light intensities in the application: +</p> + +<pre><code> +lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); +lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // darken diffuse light a bit +lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); +</code></pre> + +<p> + Now that we modulated how the light influences the object's material we get a visual output that looks much like the output from the previous chapter. This time however we got full control over the lighting and the material of the object: +</p> + +<img src="/img/lighting/materials_light.png" class="clean"/> + +<p> + Changing the visual aspects of objects is relatively easy right now. Let's spice things up a bit! +</p> + +<h2>Different light colors</h2> +<p> + So far we used light colors to only vary the intensity of their individual components by choosing colors that range from white to gray to black, not affecting the actual colors of the object (only its intensity). Since we now have easy access to the light's properties we can change their colors over time to get some really interesting effects. Since everything is already set up in the fragment shader, changing the light's colors is easy and immediately creates some funky effects: +</p> + +<div class="video paused" onclick="ClickVideo(this)"> + <video width="600" height="450" loop> + <source src="/video/lighting/materials.mp4" type="video/mp4" /> + <img src="/img/lighting/materials_light_colors.png"/> + </video> +</div> + + +<p> + As you can see, a different light color greatly influences the object's color output. Since the light color directly influences what colors the object can reflect (as you may remember from the <a href="https://learnopengl.com/Lighting/Colors" target="_blank">Colors</a> chapter) it has a significant impact on the visual output. +</p> + +<p> + We can easily change the light's colors over time by changing the light's ambient and diffuse colors via <fun>sin</fun> and <fun><function id='47'>glfwGetTime</function></fun>: +</p> + +<pre><code> +glm::vec3 lightColor; +lightColor.x = sin(<function id='47'>glfwGetTime</function>() * 2.0f); +lightColor.y = sin(<function id='47'>glfwGetTime</function>() * 0.7f); +lightColor.z = sin(<function id='47'>glfwGetTime</function>() * 1.3f); + +glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f); +glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); + +lightingShader.setVec3("light.ambient", ambientColor); +lightingShader.setVec3("light.diffuse", diffuseColor); +</code></pre> + +<p> + Try and experiment with several lighting and material values and see how they affect the visual output. You can find the source code of the application <a href="/code_viewer_gh.php?code=src/2.lighting/3.1.materials/materials.cpp" target="_blank">here</a>. +</p> + +<h2>Exercises</h2> +<p> + <ul> + <li>Can you make it so that changing the light color changes the color of the light's cube object?</li> + <li>Can you simulate some of the real-world objects by defining their respective materials like we've seen at the start of this chapter? Note that the <a href="http://devernay.free.fr/cours/opengl/materials.html" target="_blank">table</a>'s ambient values are not the same as the diffuse values; they didn't take light intensities into account. To correctly set their values you'd have to set all the light intensities to <code>vec3(1.0)</code> to get the same output: <a href="/code_viewer_gh.php?code=src/2.lighting/3.2.materials_exercise1/materials_exercise1.cpp" target="_blank">solution</a> of cyan plastic container.</li> + </ul> +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Multiple-lights.html b/pub/Lighting/Multiple-lights.html @@ -0,0 +1,589 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Multiple lights</h1> +<h1 id="content-url" style='display:none;'>Lighting/Multiple-lights</h1> +<p> + 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. +</p> + +<p> + 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. +</p> + +<p> + 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. +</p> + +<p> + 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: +</p> + +<pre><code> +out vec4 FragColor; + +void main() +{ + // define an output color value + vec3 output = vec3(0.0); + // add the directional light's contribution to the output + output += someFunctionToCalculateDirectionalLight(); + // do the same for all point lights + for(int i = 0; i &lt nr_of_point_lights; i++) + output += someFunctionToCalculatePointLight(); + // and add others lights as well (like spotlights) + output += someFunctionToCalculateSpotLight(); + + FragColor = vec4(output, 1.0); +} +</code></pre> + +<p> + 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. +</p> + +<h2>Directional light</h2> +<p> + 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. +</p> + +<p> + 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: +</p> + +<pre><code> +struct DirLight { + vec3 direction; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; +uniform DirLight dirLight; +</code></pre> + +<p> + We can then pass the <var>dirLight</var> uniform to a function with the following prototype: +</p> + +<pre><code> +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir); +</code></pre> + +<note> + 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. +</note> + +<p> + 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: +</p> + +<pre><code> +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) +{ + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // combine results + vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); + return (ambient + diffuse + specular); +} +</code></pre> + +<p> + 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. +</p> + +<h2>Point light</h2> +<p> + 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: +</p> + +<pre><code> +struct PointLight { + vec3 position; + + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; +#define NR_POINT_LIGHTS 4 +uniform PointLight pointLights[NR_POINT_LIGHTS]; +</code></pre> + +<p> + 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. +</p> + +<!--<note> + 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. +</note>--> + +<p> + The prototype of the point light's function is as follows: +</p> + +<pre><code> +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); +</code></pre> + +<p> + 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: +</p> + +<pre><code> +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 lightDir = normalize(light.position - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + + light.quadratic * (distance * distance)); + // combine results + vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular); +} +</code></pre> + +<p> + 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. +</p> + +<h2>Putting it all together</h2> +<p> + 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. +</p> + +<pre><code> +void main() +{ + // properties + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(viewPos - FragPos); + + // phase 1: Directional lighting + vec3 result = CalcDirLight(dirLight, norm, viewDir); + // phase 2: Point lights + for(int i = 0; i &lt; NR_POINT_LIGHTS; i++) + result += CalcPointLight(pointLights[i], norm, FragPos, viewDir); + // phase 3: Spot light + //result += CalcSpotLight(spotLight, norm, FragPos, viewDir); + + FragColor = vec4(result, 1.0); +} +</code></pre> + +<p> + 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. +</p> + +<note> + 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. +</note> + +<p> + 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. +</p> + +<p> + 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: +</p> + +<pre><code> +lightingShader.setFloat("pointLights[0].constant", 1.0f); +</code></pre> + +<p> + 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>. +</p> + +<p> + 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: +</p> + +<pre><code> +glm::vec3 pointLightPositions[] = { + glm::vec3( 0.7f, 0.2f, 2.0f), + glm::vec3( 2.3f, -3.3f, -4.0f), + glm::vec3(-4.0f, 2.0f, -12.0f), + glm::vec3( 0.0f, 0.0f, -3.0f) +}; +</code></pre> + +<p> + 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. +</p> + +<p> + If you'd also use a flashlight, the result of all the combined lights looks something like this: +</p> + +<img src="/img/lighting/multiple_lights_combined.png" class="clean"/> + +<p> + 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? +</p> + +<p> + 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>. +</p> + +<p> + 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: +</p> + +<img src="/img/lighting/multiple_lights_atmospheres.png" class="clean" style="border-radius: 0px;"/> + +<p> + 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. +</p> + +<p> + 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. +</p> + +<h2>Exercises</h2> +<p> + <ul> + <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> + </ul> +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Lighting/Review.html b/pub/Lighting/Review.html @@ -0,0 +1,362 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Review</h1> +<h1 id="content-url" style='display:none;'>Lighting/Review</h1> +<p> + Congratulations on making it this far! I'm not sure if you noticed, but over all the lighting chapters we learned nothing new about OpenGL itself aside from a few minor items like accessing uniform arrays. All of the lighting chapters so far were all about manipulating shaders using techniques and equations to achieve realistic lighting results. This again shows you the power of shaders. Shaders are extremely flexible and you witnessed first-hand that with just a few 3D vectors and some configurable variables we were able to create amazing graphics! +</p> + +<p> + The last few chapters you learned about colors, the Phong lighting model (that includes ambient, diffuse and specular lighting), object materials, configurable light properties, diffuse and specular maps, different types of lights, and how to combine all the knowledge into a single fully lit scene. Be sure to experiment with different lights, material colors, light properties, and try to create your own environments with the help of a little bit of creativity. +</p> + +<p> + In the next chapters we'll be adding more advanced geometry shapes to our scene that look really well in the lighting models we've discussed. +</p> + +<h2>Glossary</h2> +<p> +<ul> + <li><code>Color vector</code>: a vector portraying most of the real world colors via a combination of red, green and blue components (abbreviated to <code>RGB</code>). The color of an object is the reflected color components that an object did not absorb.</li> + <li><code>Phong lighting model</code>: a model for approximating real-world lighting by computing an ambient, diffuse and specular component.</li> + <li><code>Ambient lighting</code>: approximation of global illumination by giving each object a small brightness so that objects aren't completely dark if not directly lit.</li> + <li><code>Diffuse shading</code>: lighting that gets stronger the more a vertex/fragment is aligned to a light source. Makes use of normal vectors to calculate the angles.</li> + <li><code>Normal vector</code>: a unit vector that is perpendicular to a surface.</li> + <li><code>Normal matrix</code>: a 3x3 matrix that is the model (or model-view) matrix without translation. It is also modified in such a way (inverse-transpose) that it keeps normal vectors facing in the correct direction when applying non-uniform scaling. Otherwise normal vectors get distorted when using non-uniform scaling.</li> + <li><code>Specular lighting</code>: sets a specular highlight the closer the viewer is looking at the reflection of a light source on a surface. Based on the viewer's direction, the light's direction and a shininess value that sets the amount of scattering of the highlight.</li> + <li><code>Phong shading</code>: the Phong lighting model applied in the fragment shader.</li> + <li><code>Gouraud shading</code>: the Phong lighting model applied in the vertex shader. Produces noticeable artifacts when using a small number of vertices. Gains efficiency for loss of visual quality.</li> + <li><code>GLSL struct</code>: a C-like struct that acts as a container for shader variables. Mostly used for organizing input, output, and uniforms.</li> + <li><code>Material</code>: the ambient, diffuse and specular color an object reflects. These set the colors an object has.</li> + <li><code>Light (properties)</code>: the ambient, diffuse and specular intensity of a light. These can take any color value and define at what color/intensity a light source shines for each specific Phong component.</li> + <li><code>Diffuse map</code>: a texture image that sets the diffuse color per fragment.</li> + <li><code>Specular map</code>: a texture map that sets the specular intensity/color per fragment. Allows for specular highlights only on certain areas of an object.</li> + <li><code>Directional light</code>: a light source with only a direction. It is modeled to be at an infinite distance which has the effect that all its light rays seem parallel and its direction vector thus stays the same over the entire scene. </li> + <li><code>Point light</code>: a light source with a location in a scene with light that fades out over distance.</li> + <li><code>Attenuation</code>: the process of light reducing its intensity over distance, used in point lights and spotlights.</li> + <li><code>Spotlight</code>: a light source that is defined by a cone in one specific direction.</li> + <li><code>Flashlight</code>: a spotlight positioned from the viewer's perspective.</li> + <li><code>GLSL uniform array</code>: an array of uniform values. Work just like a C-array, except that they can't be dynamically allocated. </li> +</ul> +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Model-Loading/Assimp.html b/pub/Model-Loading/Assimp.html @@ -0,0 +1,412 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Assimp</h1> +<h1 id="content-url" style='display:none;'>Model-Loading/Assimp</h1> +<p> + In all the scenes so far we've been extensively playing with our little container friend, but over time, even our best friends can get a little boring. In bigger graphics applications, there are usually lots of complicated and interesting models that are much prettier to look at than a static container. However, unlike the container object, we can't really manually define all the vertices, normals, and texture coordinates of complicated shapes like houses, vehicles, or human-like characters. What we want instead, is to <em>import</em> these models into the application; models that were carefully designed by 3D artists in tools like <a href="http://www.blender.org/" target="_blank">Blender</a>, <a href="http://www.autodesk.nl/products/3ds-max/overview" target="_blank">3DS Max</a> or <a href="http://www.autodesk.com/products/autodesk-maya/overview" target="_blank">Maya</a>. +</p> + +<p> + These so called <def>3D modeling tools</def> allow artists to create complicated shapes and apply textures to them via a process called <def>uv-mapping</def>. The tools then automatically generate all the vertex coordinates, vertex normals, and texture coordinates while exporting them to a model file format we can use. This way, artists have an extensive toolkit to create high quality models without having to care too much about the technical details. All the technical aspects are hidden in the exported model file. We, as graphics programmers, <strong>do</strong> have to care about these technical details though. +</p> + +<p> + It is our job to parse these exported model files and extract all the relevant information so we can store them in a format that OpenGL understands. A common issue is that there are dozens of different file formats where each exports the model data in its own unique way. Model formats like the <a href="http://en.wikipedia.org/wiki/Wavefront_.obj_file" target="_blank">Wavefront .obj</a> only contains model data with minor material information like model colors and diffuse/specular maps, while model formats like the XML-based <a href="http://en.wikipedia.org/wiki/COLLADA" target="_blank">Collada file format</a> are extremely extensive and contain models, lights, many types of materials, animation data, cameras, complete scene information, and much more. The wavefront object format is generally considered to be an easy-to-parse model format. It is recommended to visit the Wavefront's wiki page at least once to see how such a file format's data is structured. This should give you a basic perception of how model file formats are generally structured. +</p> + +<p> + All by all, there are many different file formats where a common general structure between them usually does not exist. So if we want to import a model from these file formats, we'd have to write an importer ourselves for each of the file formats we want to import. Luckily for us, there just happens to be a library for this. +</p> + +<h2>A model loading library</h2> +<p> + A very popular model importing library out there is called <a href="http://assimp.org/" target="_blank">Assimp</a> that stands for <em>Open Asset Import Library</em>. Assimp is able to import dozens of different model file formats (and export to some as well) by loading all the model's data into Assimp's generalized data structures. As soon as Assimp has loaded the model, we can retrieve all the data we need from Assimp's data structures. Because the data structure of Assimp stays the same, regardless of the type of file format we imported, it abstracts us from all the different file formats out there. +</p> + +<p> + When importing a model via Assimp it loads the entire model into a <em>scene</em> object that contains all the data of the imported model/scene. Assimp then has a collection of nodes where each node contains indices to data stored in the scene object where each node can have any number of children. A (simplistic) model of Assimp's structure is shown below: +</p> + +<img src="/img/model_loading/assimp_structure.png" class="clean"/> + +<p> + <ul> + <li>All the data of the scene/model is contained in the <u>Scene</u> object like all the materials and the meshes. It also contains a reference to the root node of the scene.</li> + <li>The <u>Root node</u> of the scene may contain children nodes (like all other nodes) and could have a set of indices that point to mesh data in the scene object's <var>mMeshes</var> array. The scene's <var>mMeshes</var> array contains the actual <u>Mesh</u> objects, the values in the <var>mMeshes</var> array of a node are only indices for the scene's meshes array.</li> + <li>A <u>Mesh</u> object itself contains all the relevant data required for rendering, think of vertex positions, normal vectors, texture coordinates, faces, and the material of the object.</li> + <li>A mesh contains several faces. A <u>Face</u> represents a render primitive of the object (triangles, squares, points). A face contains the indices of the vertices that form a primitive. Because the vertices and the indices are separated, this makes it easy for us to render via an index buffer (see <a href="https://learnopengl.com/Getting-started/Hello-Triangle" target="_blank">Hello Triangle</a>).</li> + <li>Finally a mesh also links to a <u>Material</u> object that hosts several functions to retrieve the material properties of an object. Think of colors and/or texture maps (like diffuse and specular maps).</li> + </ul> +</p> + +<p> + What we want to do is: first load an object into a <u>Scene</u> object, recursively retrieve the corresponding <u>Mesh</u> objects from each of the nodes (we recursively search each node's children), and process each <u>Mesh</u> object to retrieve the vertex data, indices, and its material properties. The result is then a collection of mesh data that we want to contain in a single <code>Model</code> object. +</p> + +<note> + <strong>Mesh</strong><br/> + When modeling objects in modeling toolkits, artists generally do not create an entire model out of a single shape. Usually, each model has several sub-models/shapes that it consists of. Each of those single shapes is called a <def>mesh</def>. Think of a human-like character: artists usually model the head, limbs, clothes, and weapons all as separate components, and the combined result of all these meshes represents the final model. A single mesh is the minimal representation of what we need to draw an object in OpenGL (vertex data, indices, and material properties). A model (usually) consists of several meshes. +</note> + +<p> + In the <a href="https://learnopengl.com/Model-Loading/Mesh" target="_blank">next</a> chapters we'll create our own <fun>Model</fun> and <fun>Mesh</fun> class that load and store imported models using the structure we've just described. If we then want to draw a model, we do not render the model as a whole, but we render all of the individual meshes that the model is composed of. However, before we can start importing models, we first need to actually include Assimp in our project. +</p> + +<h2>Building Assimp</h2> +<p> + You can download Assimp from their <a href="http://assimp.org/index.php/downloads" target="_blank">download</a> page and choose the corresponding version. For this writing, the Assimp version used was version <code>3.1.1</code>. It is advised to compile the libraries by yourself, since their pre-compiled libraries don't always work on all systems. Review the <a href="https://learnopengl.com/Getting-started/Creating-a-window" target="_blank">Creating a window</a> chapter if you forgot how to compile a library by yourself via CMake. +</p> + +<p> + A few issues can come up while building Assimp, so I'll note them down here with their solutions in case any of you get the same errors: + <ul> + <li>CMake continually gives errors while retrieving the configuration list about DirectX libraries missing, messages like: +<pre><code> +Could not locate DirectX +CMake Error at cmake-modules/FindPkgMacros.cmake:110 (message): +Required library DirectX not found! Install the library (including dev packages) +and try again. If the library is already installed, set the missing variables +manually in cmake. +</code></pre> + The solution here is to install the DirectX SDK in case you haven't installed this before. You can download the SDK from <a href="http://www.microsoft.com/en-us/download/details.aspx?id=6812" target="_blank">here</a>.</li> + <li>While installing the DirectX SDK, a possible error code of <code>s1023</code> could pop up. In that case you first want to de-install the C++ Redistributable package(s) before installing the SDK.</li> + </ul> +</p> + +<p> + Once the configuration is completed, you can generate a solution file, open it, and compile the libraries (either as a release version or a debug version, whatever floats your boat). Be sure to compile it for 64-bit as all LearnOpenGL code is 64 bit. +</p> + +<p> + The default configuration builds Assimp as a dynamic library so we need to include the resulting DLL named <code>assimp.dll</code> (or with some post-fix) alongside the application's binaries. You can simply copy the DLL to the same folder where your application's executable is located. +</p> + +<p> + After compiling the generated solution, the resulting library and DLL file are located in the <code>code/Debug</code> or <code>code/Release</code> folder. Then simply move the lib and DLL to their appropriate locations, link them from your solution, and be sure to copy Assimp's headers to your <code>include</code> directory (the header files are found in the <code>include</code> folder in the files downloaded from Assimp). +</p> + +<p> + By now you should have compiled Assimp and linked it to your application. If you still received any unreported error, feel free to ask for help in the comments. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/Model-Loading/Mesh.html b/pub/Model-Loading/Mesh.html @@ -0,0 +1,562 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Mesh</h1> +<h1 id="content-url" style='display:none;'>Model-Loading/Mesh</h1> +<p> + With Assimp we can load many different models into the application, but once loaded they're all stored in Assimp's data structures. What we eventually want is to transform that data to a format that OpenGL understands so that we can render the objects. We learned from the previous chapter that a mesh represents a single drawable entity, so let's start by defining a mesh class of our own. +</p> + +<p> + Let's review a bit of what we've learned so far to think about what a mesh should minimally have as its data. A mesh should at least need a set of vertices, where each vertex contains a position vector, a normal vector, and a texture coordinate vector. A mesh should also contain indices for indexed drawing, and material data in the form of textures (diffuse/specular maps). +</p> + +<p> + Now that we set the minimal requirements for a mesh class we can define a vertex in OpenGL: +</p> + +<pre><code> +struct Vertex { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec2 TexCoords; +}; +</code></pre> + +<p> + We store each of the required vertex attributes in a struct called <fun>Vertex</fun>. Next to a <fun>Vertex</fun> struct we also want to organize the texture data in a <fun>Texture</fun> struct: +</p> + +<pre><code> +struct Texture { + unsigned int id; + string type; +}; +</code></pre> + +<p> + We store the id of the texture and its type e.g. a diffuse or specular texture. +</p> + +<p> + Knowing the actual representation of a vertex and a texture we can start defining the structure of the mesh class: +</p> + +<pre><code> +class Mesh { + public: + // mesh data + vector&lt;Vertex&gt; vertices; + vector&lt;unsigned int&gt; indices; + vector&lt;Texture&gt; textures; + + Mesh(vector&lt;Vertex&gt; vertices, vector&lt;unsigned int&gt; indices, vector&lt;Texture&gt; textures); + void Draw(Shader &shader); + private: + // render data + unsigned int VAO, VBO, EBO; + + void setupMesh(); +}; +</code></pre> + +<p> + As you can see, the class isn't too complicated. In the constructor we give the mesh all the necessary data, we initialize the buffers in the <fun>setupMesh</fun> function, and finally draw the mesh via the <fun>Draw</fun> function. Note that we give a shader to the <fun>Draw</fun> function; by passing the shader to the mesh we can set several uniforms before drawing (like linking samplers to texture units). +</p> + +<p> + The function content of the constructor is pretty straightforward. We simply set the class's public variables with the constructor's corresponding argument variables. We also call the <fun>setupMesh</fun> function in the constructor: +</p> + +<pre><code> +Mesh(vector&lt;Vertex&gt; vertices, vector&lt;unsigned int&gt; indices, vector&lt;Texture&gt; textures) +{ + this-&gt;vertices = vertices; + this-&gt;indices = indices; + this-&gt;textures = textures; + + setupMesh(); +} +</code></pre> + +<p> + Nothing special going on here. Let's delve right into the <fun>setupMesh</fun> function now. +</p> + +<h2>Initialization</h2> +<p> + Thanks to the constructor we now have large lists of mesh data that we can use for rendering. We do need to setup the appropriate buffers and specify the vertex shader layout via vertex attribute pointers. By now you should have no trouble with these concepts, but we've spiced it up a bit this time with the introduction of vertex data in structs: +</p> + +<pre><code> +void setupMesh() +{ + <function id='33'>glGenVertexArrays</function>(1, &VAO); + <function id='12'>glGenBuffers</function>(1, &VBO); + <function id='12'>glGenBuffers</function>(1, &EBO); + + <function id='27'>glBindVertexArray</function>(VAO); + <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO); + + <function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); + + <function id='32'>glBindBuffer</function>(GL_ELEMENT_ARRAY_BUFFER, EBO); + <function id='31'>glBufferData</function>(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), + &indices[0], GL_STATIC_DRAW); + + // vertex positions + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0); + <function id='30'>glVertexAttribPointer</function>(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); + // vertex normals + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(1); + <function id='30'>glVertexAttribPointer</function>(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); + // vertex texture coords + <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(2); + <function id='30'>glVertexAttribPointer</function>(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords)); + + <function id='27'>glBindVertexArray</function>(0); +} +</code></pre> + +<p> + The code is not much different from what you'd expect, but a few little tricks were used with the help of the <fun>Vertex</fun> struct. +</p> + +<p> + Structs have a great property in C++ that their memory layout is sequential. That is, if we were to represent a struct as an array of data, it would only contain the struct's variables in sequential order which directly translates to a float (actually byte) array that we want for an array buffer. For example, if we have a filled <fun>Vertex</fun> struct, its memory layout would be equal to: +</p> + +<pre><code> +Vertex vertex; +vertex.Position = glm::vec3(0.2f, 0.4f, 0.6f); +vertex.Normal = glm::vec3(0.0f, 1.0f, 0.0f); +vertex.TexCoords = glm::vec2(1.0f, 0.0f); +// = [0.2f, 0.4f, 0.6f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f]; +</code></pre> + +<p> + Thanks to this useful property we can directly pass a pointer to a large list of <fun>Vertex</fun> structs as the buffer's data and they translate perfectly to what <fun><function id='31'>glBufferData</function></fun> expects as its argument: +</p> + +<pre><code> +<function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices[0], GL_STATIC_DRAW); +</code></pre> + +<p> + Naturally the <code>sizeof</code> operator can also be used on the struct for the appropriate size in bytes. This should be <code>32</code> bytes (<code>8</code> floats * <code>4</code> bytes each). +</p> + +<p> + Another great use of structs is a preprocessor directive called <code>offsetof(s,m)</code> that takes as its first argument a struct and as its second argument a variable name of the struct. The macro returns the byte offset of that variable from the start of the struct. This is perfect for defining the offset parameter of the <fun><function id='30'>glVertexAttribPointer</function></fun> function: +</p> + +<pre><code> +<function id='30'>glVertexAttribPointer</function>(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); +</code></pre> + +<p> + The offset is now defined using the <fun>offsetof</fun> macro that, in this case, sets the byte offset of the normal vector equal to the byte offset of the normal attribute in the struct which is <code>3</code> floats and thus <code>12</code> bytes. +</p> + +<p> + Using a struct like this doesn't only get us more readable code, but also allows us to easily extend the structure. If we want another vertex attribute we can simply add it to the struct and due to its flexible nature, the rendering code won't break. +</p> + +<h2>Rendering</h2> +<p> + The last function we need to define for the <fun>Mesh</fun> class to be complete is its <fun>Draw</fun> function. Before rendering the mesh, we first want to bind the appropriate textures before calling <fun><function id='2'>glDrawElements</function></fun>. However, this is somewhat difficult since we don't know from the start how many (if any) textures the mesh has and what type they may have. So how do we set the texture units and samplers in the shaders? +</p> + +<p> + To solve the issue we're going to assume a certain naming convention: each diffuse texture is named <code>texture_diffuseN</code>, and each specular texture should be named <code>texture_specularN</code> where <code>N</code> is any number ranging from <code>1</code> to the maximum number of texture samplers allowed. Let's say we have 3 diffuse textures and 2 specular textures for a particular mesh, their texture samplers should then be called: +</p> + +<pre><code> +uniform sampler2D texture_diffuse1; +uniform sampler2D texture_diffuse2; +uniform sampler2D texture_diffuse3; +uniform sampler2D texture_specular1; +uniform sampler2D texture_specular2; +</code></pre> + +<p> + By this convention we can define as many texture samplers as we want in the shaders (up to OpenGL's maximum) and if a mesh actually does contain (so many) textures, we know what their names are going to be. By this convention we can process any amount of textures on a single mesh and the shader developer is free to use as many of those as he wants by defining the proper samplers. +</p> + +<note> + There are many solutions to problems like this and if you don't like this particular solution it is up to you to get creative and come up with your own approach. +</note> + +<p> + The resulting drawing code then becomes: +</p> + +<pre><code> +void Draw(Shader &shader) +{ + unsigned int diffuseNr = 1; + unsigned int specularNr = 1; + for(unsigned int i = 0; i &lt; textures.size(); i++) + { + <function id='49'>glActiveTexture</function>(GL_TEXTURE0 + i); // activate proper texture unit before binding + // retrieve texture number (the N in diffuse_textureN) + string number; + string name = textures[i].type; + if(name == "texture_diffuse") + number = std::to_string(diffuseNr++); + else if(name == "texture_specular") + number = std::to_string(specularNr++); + + shader.setFloat(("material." + name + number).c_str(), i); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, textures[i].id); + } + <function id='49'>glActiveTexture</function>(GL_TEXTURE0); + + // draw mesh + <function id='27'>glBindVertexArray</function>(VAO); + <function id='2'>glDrawElements</function>(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); + <function id='27'>glBindVertexArray</function>(0); +} +</code></pre> + +<p> + We first calculate the N-component per texture type and concatenate it to the texture's type string to get the appropriate uniform name. We then locate the appropriate sampler, give it the location value to correspond with the currently active texture unit, and bind the texture. This is also the reason we need the shader in the <fun>Draw</fun> function. +</p> + +<p> + We also added <code>"material."</code> to the resulting uniform name because we usually store the textures in a material struct (this may differ per implementation). +</p> + +<note> + Note that we increment the diffuse and specular counters the moment we convert them to <code>string</code>. In C++ the increment call: <code>variable++</code> returns the variable as is and <strong>then</strong> increments the variable while <code>++variable</code> <strong>first</strong> increments the variable and <strong>then</strong> returns it. In our case the value passed to <code>std::string</code> is the original counter value. After that the value is incremented for the next round. +</note> + +<p> + You can find the full source code of the <fun>Mesh</fun> class <a href="/code_viewer_gh.php?code=includes/learnopengl/mesh.h" target="_blank">here</a>. +</p> + +<p> + The <fun>Mesh</fun> class we just defined is an abstraction for many of the topics we've discussed in the early chapters. In the <a href="https://learnopengl.com/Model-Loading/Model" target="_blank">next</a> chapter we'll create a model that acts as a container for several mesh objects and implements Assimp's loading interface. +</p> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Model-Loading/Model.html b/pub/Model-Loading/Model.html @@ -0,0 +1,748 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <div id="content"> + <h1 id="content-title">Model</h1> +<h1 id="content-url" style='display:none;'>Model-Loading/Model</h1> +<p> + Now it is time to get our hands dirty with Assimp and start creating the actual loading and translation code. The goal of this chapter is to create another class that represents a model in its entirety, that is, a model that contains multiple meshes, possibly with multiple textures. A house, that contains a wooden balcony, a tower, and perhaps a swimming pool, could still be loaded as a single model. We'll load the model via Assimp and translate it to multiple <fun>Mesh</fun> objects we've created in the <a href="https://learnopengl.com/Model-Loading/Mesh" target="_blank">previous</a> chapter. +</p> + +<p> + Without further ado, I present you the class structure of the <fun>Model</fun> class: +</p> + +<pre><code> +class Model +{ + public: + Model(char *path) + { + loadModel(path); + } + void Draw(Shader &shader); + private: + // model data + vector&lt;Mesh&gt; meshes; + string directory; + + void loadModel(string path); + void processNode(aiNode *node, const aiScene *scene); + Mesh processMesh(aiMesh *mesh, const aiScene *scene); + vector&lt;Texture&gt; loadMaterialTextures(aiMaterial *mat, aiTextureType type, + string typeName); +}; +</code></pre> + +<p> + The <fun>Model</fun> class contains a vector of <fun>Mesh</fun> objects and requires us to give it a file location in its constructor. It then loads the file right away via the <fun>loadModel</fun> function that is called in the constructor. The private functions are all designed to process a part of Assimp's import routine and we'll cover them shortly. We also store the directory of the file path that we'll later need when loading textures. +</p> + +<p> + The <fun>Draw</fun> function is nothing special and basically loops over each of the meshes to call their respective <fun>Draw</fun> function: +</p> + +<pre><code> +void Draw(Shader &shader) +{ + for(unsigned int i = 0; i &lt; meshes.size(); i++) + meshes[i].Draw(shader); +} +</code></pre> + +<h2>Importing a 3D model into OpenGL</h2> +<p> + To import a model and translate it to our own structure, we first need to include the appropriate headers of Assimp: +</p> + +<pre><code> +#include &lt;assimp/Importer.hpp&gt; +#include &lt;assimp/scene.h&gt; +#include &lt;assimp/postprocess.h&gt; +</code></pre> + +<p> + The first function we're calling is <fun>loadModel</fun>, that's directly called from the constructor. Within <fun>loadModel</fun>, we use Assimp to load the model into a data structure of Assimp called a <u>scene</u> object. You may remember from the <a href="https://learnopengl.com/Model-Loading/Assimp" target="_blank">first</a> chapter of the model loading series that this is the root object of Assimp's data interface. Once we have the scene object, we can access all the data we need from the loaded model. +</p> + +<p> + The great thing about Assimp is that it neatly abstracts from all the technical details of loading all the different file formats and does all this with a single one-liner: +</p> + +<pre><code> +Assimp::Importer importer; +const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); +</code></pre> + +<p> + We first declare an <fun>Importer</fun> object from Assimp's namespace and then call its <fun>ReadFile</fun> function. The function expects a file path and several <def>post-processing</def> options as its second argument. Assimp allows us to specify several options that forces Assimp to do extra calculations/operations on the imported data. By setting <var>aiProcess_Triangulate</var> we tell Assimp that if the model does not (entirely) consist of triangles, it should transform all the model's primitive shapes to triangles first. The <var>aiProcess_FlipUVs</var> flips the texture coordinates on the y-axis where necessary during processing (you may remember from the <a href="https://learnopengl.com/Getting-started/Textures" target="_blank">Textures</a> chapter that most images in OpenGL were reversed around the y-axis; this little postprocessing option fixes that for us). A few other useful options are: + + <ul> + <li><var>aiProcess_GenNormals</var>: creates normal vectors for each vertex if the model doesn't contain normal vectors.</li> + <li><var>aiProcess_SplitLargeMeshes</var>: splits large meshes into smaller sub-meshes which is useful if your rendering has a maximum number of vertices allowed and can only process smaller meshes.</li> + <li><var>aiProcess_OptimizeMeshes</var>: does the reverse by trying to join several meshes into one larger mesh, reducing drawing calls for optimization.</li> + </ul> + + Assimp provides a great set of postprocessing options and you can find all of them <a href="http://assimp.sourceforge.net/lib_html/postprocess_8h.html" target="_blank">here</a>. Loading a model via Assimp is (as you can see) surprisingly easy. The hard work is in using the returned scene object to translate the loaded data to an array of <code>Mesh</code> objects. +</p> + +<p> + The complete <fun>loadModel</fun> function is listed here: +</p> + +<pre><code> +void loadModel(string path) +{ + Assimp::Importer import; + const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); + + if(!scene || scene-&gt;mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene-&gt;mRootNode) + { + cout &lt;&lt; "ERROR::ASSIMP::" &lt;&lt; import.GetErrorString() &lt;&lt; endl; + return; + } + directory = path.substr(0, path.find_last_of('/')); + + processNode(scene-&gt;mRootNode, scene); +} +</code></pre> + +<p> + After we load the model, we check if the scene and the root node of the scene are not null and check one of its flags to see if the returned data is incomplete. If any of these error conditions are met, we report the error retrieved from the importer's <fun>GetErrorString</fun> function and return. We also retrieve the directory path of the given file path. +</p> + +<p> + If nothing went wrong, we want to process all of the scene's nodes. We pass the first node (root node) to the recursive <fun>processNode</fun> function. Because each node (possibly) contains a set of children we want to first process the node in question, and then continue processing all the node's children and so on. This fits a recursive structure, so we'll be defining a recursive function. A recursive function is a function that does some processing and <def>recursively</def> calls the same function with different parameters until a certain condition is met. In our case the <def>exit condition</def> is met when all nodes have been processed. +</p> + +<p> + As you may remember from Assimp's structure, each node contains a set of mesh indices where each index points to a specific mesh located in the scene object. We thus want to retrieve these mesh indices, retrieve each mesh, process each mesh, and then do this all again for each of the node's children nodes. The content of the <fun>processNode</fun> function is shown below: +</p> + +<pre><code> +void processNode(aiNode *node, const aiScene *scene) +{ + // process all the node's meshes (if any) + for(unsigned int i = 0; i &lt; node-&gt;mNumMeshes; i++) + { + aiMesh *mesh = scene-&gt;mMeshes[node-&gt;mMeshes[i]]; + meshes.push_back(processMesh(mesh, scene)); + } + // then do the same for each of its children + for(unsigned int i = 0; i &lt; node-&gt;mNumChildren; i++) + { + processNode(node-&gt;mChildren[i], scene); + } +} +</code></pre> + +<p> + We first check each of the node's mesh indices and retrieve the corresponding mesh by indexing the scene's <var>mMeshes</var> array. The returned mesh is then passed to the <fun>processMesh</fun> function that returns a <fun>Mesh</fun> object that we can store in the <var>meshes</var> list/vector. +</p> + +<p> + Once all the meshes have been processed, we iterate through all of the node's children and call the same <fun>processNode</fun> function for each its children. Once a node no longer has any children, the recursion stops. +</p> + +<note> + A careful reader may have noticed that we could forget about processing any of the nodes and simply loop through all of the scene's meshes directly, without doing all this complicated stuff with indices. The reason we're doing this is that the initial idea for using nodes like this is that it defines a parent-child relation between meshes. By recursively iterating through these relations, we can define certain meshes to be parents of other meshes.<br/> + An example use case for such a system is when you want to translate a car mesh and make sure that all its children (like an engine mesh, a steering wheel mesh, and its tire meshes) translate as well; such a system is easily created using parent-child relations.<br/><br/> + Right now however we're not using such a system, but it is generally recommended to stick with this approach for whenever you want extra control over your mesh data. These node-like relations are after all defined by the artists who created the models. +</note> + +<p> + The next step is to process Assimp's data into the <fun>Mesh</fun> class from the previous chapter. +</p> + +<h3>Assimp to Mesh</h3> +<p> + Translating an <code>aiMesh</code> object to a mesh object of our own is not too difficult. All we need to do, is access each of the mesh's relevant properties and store them in our own object. The general structure of the <fun>processMesh</fun> function then becomes: +</p> + +<pre><code> +Mesh processMesh(aiMesh *mesh, const aiScene *scene) +{ + vector&lt;Vertex&gt; vertices; + vector&lt;unsigned int&gt; indices; + vector&lt;Texture&gt; textures; + + for(unsigned int i = 0; i &lt; mesh-&gt;mNumVertices; i++) + { + Vertex vertex; + // process vertex positions, normals and texture coordinates + [...] + vertices.push_back(vertex); + } + // process indices + [...] + // process material + if(mesh-&gt;mMaterialIndex &gt;= 0) + { + [...] + } + + return Mesh(vertices, indices, textures); +} +</code></pre> + +<p> + Processing a mesh is a 3-part process: retrieve all the vertex data, retrieve the mesh's indices, and finally retrieve the relevant material data. The processed data is stored in one of the <code>3</code> vectors and from those a <fun>Mesh</fun> is created and returned to the function's caller. +</p> + +<p> + Retrieving the vertex data is pretty simple: we define a <fun>Vertex</fun> struct that we add to the <var>vertices</var> array after each loop iteration. We loop for as much vertices there exist within the mesh (retrieved via <code>mesh-&gt;mNumVertices</code>). Within the iteration we want to fill this struct with all the relevant data. For vertex positions this is done as follows: +</p> + +<pre><code> +glm::vec3 vector; +vector.x = mesh-&gt;mVertices[i].x; +vector.y = mesh-&gt;mVertices[i].y; +vector.z = mesh-&gt;mVertices[i].z; +vertex.Position = vector; +</code></pre> + +<p> + Note that we define a temporary <code>vec3</code> for transferring Assimp's data to. This is necessary as Assimp maintains its own data types for vector, matrices, strings etc. and they don't convert that well to glm's data types. +</p> + +<note> + Assimp calls their vertex position array <var>mVertices</var> which isn't the most intuitive name. +</note> + +<p> + The procedure for normals should come as no surprise now: +</p> + +<pre><code> +vector.x = mesh-&gt;mNormals[i].x; +vector.y = mesh-&gt;mNormals[i].y; +vector.z = mesh-&gt;mNormals[i].z; +vertex.Normal = vector; +</code></pre> + +<p> + Texture coordinates are roughly the same, but Assimp allows a model to have up to 8 different texture coordinates per vertex. We're not going to use 8, we only care about the first set of texture coordinates. We'll also want to check if the mesh actually contains texture coordinates (which may not be always the case): +</p> + +<pre><code> +if(mesh-&gt;mTextureCoords[0]) // does the mesh contain texture coordinates? +{ + glm::vec2 vec; + vec.x = mesh-&gt;mTextureCoords[0][i].x; + vec.y = mesh-&gt;mTextureCoords[0][i].y; + vertex.TexCoords = vec; +} +else + vertex.TexCoords = glm::vec2(0.0f, 0.0f); +</code></pre> + +<p> + The <var>vertex</var> struct is now completely filled with the required vertex attributes and we can push it to the back of the <var>vertices</var> vector at the end of the iteration. This process is repeated for each of the mesh's vertices. +</p> + +<h3>Indices</h3> +<p> + Assimp's interface defines each mesh as having an array of faces, where each face represents a single primitive, which in our case (due to the <var>aiProcess_Triangulate</var> option) are always triangles. A face contains the indices of the vertices we need to draw in what order for its primitive. So if we iterate over all the faces and store all the face's indices in the <var>indices</var> vector we're all set: +</p> + +<pre><code> +for(unsigned int i = 0; i &lt; mesh-&gt;mNumFaces; i++) +{ + aiFace face = mesh-&gt;mFaces[i]; + for(unsigned int j = 0; j &lt; face.mNumIndices; j++) + indices.push_back(face.mIndices[j]); +} +</code></pre> + +<p> + After the outer loop has finished, we now have a complete set of vertices and index data for drawing the mesh via <fun><function id='2'>glDrawElements</function></fun>. However, to finish the discussion and to add some detail to the mesh, we want to process the mesh's material as well. +</p> + +<h3>Material</h3> +<p> + Similar to nodes, a mesh only contains an index to a material object. To retrieve the material of a mesh, we need to index the scene's <var>mMaterials</var> array. The mesh's material index is set in its <var>mMaterialIndex</var> property, which we can also query to check if the mesh contains a material or not: +</p> + +<pre><code> +if(mesh-&gt;mMaterialIndex &gt;= 0) +{ + aiMaterial *material = scene-&gt;mMaterials[mesh-&gt;mMaterialIndex]; + vector&lt;Texture&gt; diffuseMaps = loadMaterialTextures(material, + aiTextureType_DIFFUSE, "texture_diffuse"); + textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); + vector&lt;Texture&gt; specularMaps = loadMaterialTextures(material, + aiTextureType_SPECULAR, "texture_specular"); + textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); +} +</code></pre> + +<p> + We first retrieve the <code>aiMaterial</code> object from the scene's <var>mMaterials</var> array. Then we want to load the mesh's diffuse and/or specular textures. A material object internally stores an array of texture locations for each texture type. The different texture types are all prefixed with <code>aiTextureType_</code>. We use a helper function called <fun>loadMaterialTextures</fun> to retrieve, load, and initialize the textures from the material. The function returns a vector of <fun>Texture</fun> structs that we store at the end of the model's <var>textures</var> vector. +</p> + +<p> + The <fun>loadMaterialTextures</fun> function iterates over all the texture locations of the given texture type, retrieves the texture's file location and then loads and generates the texture and stores the information in a <fun>Vertex</fun> struct. It looks like this: +</p> + +<pre><code> +vector&lt;Texture&gt; loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName) +{ + vector&lt;Texture&gt; textures; + for(unsigned int i = 0; i &lt; mat-&gt;GetTextureCount(type); i++) + { + aiString str; + mat-&gt;GetTexture(type, i, &str); + Texture texture; + texture.id = TextureFromFile(str.C_Str(), directory); + texture.type = typeName; + texture.path = str; + textures.push_back(texture); + } + return textures; +} +</code></pre> + +<p> + We first check the amount of textures stored in the material via its <fun>GetTextureCount</fun> function that expects one of the texture types we've given. We retrieve each of the texture's file locations via the <fun>GetTexture</fun> function that stores the result in an <code>aiString</code>. We then use another helper function called <fun>TextureFromFile</fun> that loads a texture (with <code>stb_image.h</code>) for us and returns the texture's ID. You can check the complete code listing at the end for its content if you're not sure how such a function is written. +</p> + +<note> + Note that we make the assumption that texture file paths in model files are local to the actual model object e.g. in the same directory as the location of the model itself. We can then simply concatenate the texture location string and the directory string we retrieved earlier (in the <fun>loadModel</fun> function) to get the complete texture path (that's why the <fun>GetTexture</fun> function also needs the directory string).<br/><br/>Some models found over the internet use absolute paths for their texture locations, which won't work on each machine. In that case you probably want to manually edit the file to use local paths for the textures (if possible). +</note> + +<p> + And that is all there is to importing a model with Assimp. +</p> + +<h1>An optimization</h1> +<p> + We're not completely done yet, since there is still a large (but not completely necessary) optimization we want to make. Most scenes re-use several of their textures onto several meshes; think of a house again that has a granite texture for its walls. This texture could also be applied to the floor, its ceilings, the staircase, perhaps a table, and maybe even a small well close by. Loading textures is not a cheap operation and in our current implementation a new texture is loaded and generated for each mesh, even though the exact same texture could have been loaded several times before. This quickly becomes the bottleneck of your model loading implementation. +</p> + +<p> + So we're going to add one small tweak to the model code by storing all of the loaded textures globally. Wherever we want to load a texture, we first check if it hasn't been loaded already. If so, we take that texture and skip the entire loading routine, saving us a lot of processing power. To be able to compare textures we need to store their path as well: +</p> + +<pre><code> +struct Texture { + unsigned int id; + string type; + string path; // we store the path of the texture to compare with other textures +}; +</code></pre> + +<p> + Then we store all the loaded textures in another vector declared at the top of the model's class file as a private variable: +</p> + +<pre><code> +vector&lt;Texture&gt; textures_loaded; +</code></pre> + +<p> + In the <fun>loadMaterialTextures</fun> function, we want to compare the texture path with all the textures in the <var>textures_loaded</var> vector to see if the current texture path equals any of those. If so, we skip the texture loading/generation part and simply use the located texture struct as the mesh's texture. The (updated) function is shown below: +</p> + +<pre><code> +vector&lt;Texture&gt; loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName) +{ + vector&lt;Texture&gt; textures; + for(unsigned int i = 0; i &lt; mat-&gt;GetTextureCount(type); i++) + { + aiString str; + mat-&gt;GetTexture(type, i, &str); + bool skip = false; + for(unsigned int j = 0; j &lt; textures_loaded.size(); j++) + { + if(std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0) + { + textures.push_back(textures_loaded[j]); + skip = true; + break; + } + } + if(!skip) + { // if texture hasn't been loaded already, load it + Texture texture; + texture.id = TextureFromFile(str.C_Str(), directory); + texture.type = typeName; + texture.path = str.C_Str(); + textures.push_back(texture); + textures_loaded.push_back(texture); // add to loaded textures + } + } + return textures; +} +</code></pre> + +<warning> + Some versions of Assimp tend to load models quite slow when using the debug version and/or the debug mode of your IDE, so be sure to test it out with release versions as well if you run into slow loading times. +</warning> + +<p> + You can find the complete source code of the <fun>Model</fun> class <a href="/code_viewer_gh.php?code=includes/learnopengl/model.h" target="_blank">here</a>. +</p> + +<h1>No more containers!</h1> +<p> + So let's give our implementation a spin by actually importing a model created by genuine artists, not something done by the creative genius that I am. Because I don't want to give myself too much credit, I'll occasionally allow some other artists to join the ranks and this time we're going to load this amazing <a href="https://sketchfab.com/3d-models/survival-guitar-backpack-low-poly-799f8c4511f84fab8c3f12887f7e6b36" target="_blank">Survival Guitar Backpack</a> by Berk Gedik. I've modified the material and paths a bit so it works directly with the way we've set up the model loading. The model is exported as a <code>.obj</code> file together with a <code>.mtl</code> file that links to the model's diffuse, specular, and normal maps (we'll get to those later). You can download the adjusted model for this chapter <a href="/data/models/backpack.zip" target="_blank">here</a>. Note that there's a few extra texture types we won't be using yet, and that all the textures and the model file(s) should be located in the same directory for the textures to load. +</p> + +<note> + The modified version of the backpack uses local relative texture paths, and renamed the albedo and metallic textures to diffuse and specular respectively. +</note> + +<p> + Now, declare a <fun>Model</fun> object and pass in the model's file location. The model should then automatically load and (if there were no errors) render the object in the render loop using its <fun>Draw</fun> function and that is it. No more buffer allocations, attribute pointers, and render commands, just a simple one-liner. If you create a simple set of shaders where the fragment shader only outputs the object's diffuse texture, the result looks a bit like this: +</p> + +<img src="/img/model_loading/model_diffuse.png"/> + +<p> + You can find the complete source code <a href="/code_viewer_gh.php?code=src/3.model_loading/1.model_loading/model_loading.cpp" target="_blank">here</a>. Note that we tell <code>stb_image.h</code> to flip textures vertically, if you haven't done so already, before we load the model. Otherwise the textures will look all messed up. +</p> + +<p> + We can also get more creative and introduce point lights to the render equation as we learned from the <a href="https://learnopengl.com/Lighting/Light-casters" target="_blank">Lighting</a> chapters and together with specular maps get amazing results: +</p> + +<img src="/img/model_loading/model_lighting.png"/> + +<p> + Even I have to admit that this is maybe a bit more fancy than the containers we've used so far. + Using Assimp you can load tons of models found over the internet. There are quite a few resource websites that offer free 3D models for you to download in several file formats. Do note that some models still won't load properly, have texture paths that won't work, or are simply exported in a format even Assimp can't read. +</p> + +<h2>Further reading</h2> +<ul> + <li><a href="https://www.youtube.com/watch?v=4DQquG_o-Ac" target="_blank">How-To Texture Wavefront (.obj) Models for OpenGL</a>: great video guide by Matthew Early on how to set up 3D models in Blender so they directly work with the current model loader (as the texture setup we've chosen doesn't always work out of the box).</li> +</ul> +<!-- +<h2>Exercises</h2> +<p> + <ul> + <li>Can you re-create the last scene with the two point lights?: <a href="/code_viewer.php?code=model_loading/model-exercise1" target="_blank">solution</a>, <a href="/code_viewer.php?code=model_loading/model-exercise1-shaders" target="_blank">shaders</a>.</li> + </ul> +</p> +--> + + + </div> + + </main> +</body> +</html> diff --git a/pub/PBR/IBL/Diffuse-irradiance.html b/pub/PBR/IBL/Diffuse-irradiance.html @@ -0,0 +1,992 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Diffuse irradiance</h1> +<h1 id="content-url" style='display:none;'>PBR/IBL/Diffuse-irradiance</h1> +<p> + IBL, or <def>image based lighting</def>, is a collection of techniques to light objects, not by direct analytical lights as in the <a href="https://learnopengl.com/PBR/Lighting" target="_blank">previous</a> chapter, but by treating the surrounding environment as one big light source. This is generally accomplished by manipulating a cubemap environment map (taken from the real world or generated from a 3D scene) such that we can directly use it in our lighting equations: treating each cubemap texel as a light emitter. This way we can effectively capture an environment's global lighting and general feel, giving objects a better sense of <em>belonging</em> in their environment. +</p> + +<p> + As image based lighting algorithms capture the lighting of some (global) environment, its input is considered a more precise form of ambient lighting, even a crude approximation of global illumination. This makes IBL interesting for PBR as objects look significantly more physically accurate when we take the environment's lighting into account. +</p> + +<p> + To start introducing IBL into our PBR system let's again take a quick look at the reflectance equation: +</p> + + +\[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + As described before, our main goal is to solve the integral of all incoming light directions \(w_i\) over the hemisphere \(\Omega\) . Solving the integral in the previous chapter was easy as we knew beforehand the exact few light directions \(w_i\) that contributed to the integral. + This time however, <strong>every</strong> incoming light direction \(w_i\) from the surrounding environment could potentially have some radiance making it less trivial to solve the integral. This gives us two main requirements for solving the integral: +</p> + +<ul> + <li>We need some way to retrieve the scene's radiance given any direction vector \(w_i\).</li> + <li>Solving the integral needs to be fast and real-time.</li> +</ul> + +<p> + Now, the first requirement is relatively easy. We've already hinted it, but one way of representing an environment or scene's irradiance is in the form of a (processed) environment cubemap. Given such a cubemap, we can visualize every texel of the cubemap as one single emitting light source. By sampling this cubemap with any direction vector \(w_i\), we retrieve the scene's radiance from that direction. +</p> + +<p> + Getting the scene's radiance given any direction vector \(w_i\) is then as simple as: +</p> + +<pre><code> +vec3 radiance = texture(_cubemapEnvironment, w_i).rgb; +</code></pre> + +<p> + Still, solving the integral requires us to sample the environment map from not just one direction, but all possible directions \(w_i\) over the hemisphere \(\Omega\) which is far too expensive for each fragment shader invocation. To solve the integral in a more efficient fashion we'll want to <em>pre-process</em> or <def>pre-compute</def> most of the computations. For this we'll have to delve a bit deeper into the reflectance equation: +</p> + +\[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + Taking a good look at the reflectance equation we find that the diffuse \(k_d\) and specular \(k_s\) term of the BRDF are independent from each other and we can split the integral in two: + </p> + +\[ + L_o(p,\omega_o) = + \int\limits_{\Omega} (k_d\frac{c}{\pi}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i + + + \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + By splitting the integral in two parts we can focus on both the diffuse and specular term individually; the focus of this chapter being on the diffuse integral. +</p> + +<p> + Taking a closer look at the diffuse integral we find that the diffuse lambert term is a constant term (the color \(c\), the refraction ratio \(k_d\), and \(\pi\) are constant over the integral) and not dependent on any of the integral variables. Given this, we can move the constant term out of the diffuse integral: +</p> + +\[ + L_o(p,\omega_o) = + k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + This gives us an integral that only depends on \(w_i\) (assuming \(p\) is at the center of the environment map). With this knowledge, we can calculate or <em>pre-compute</em> a new cubemap that stores in each sample direction (or texel) \(w_o\) the diffuse integral's result by <def>convolution</def>. +</p> + +<p> + Convolution is applying some computation to each entry in a data set considering all other entries in the data set; the data set being the scene's radiance or environment map. Thus for every sample direction in the cubemap, we take all other sample directions over the hemisphere \(\Omega\) into account. +</p> + +<p> + To convolute an environment map we solve the integral for each output \(w_o\) sample direction by discretely sampling a large number of directions \(w_i\) over the hemisphere \(\Omega\) and averaging their radiance. The hemisphere we build the sample directions \(w_i\) from is oriented towards the output \(w_o\) sample direction we're convoluting. +</p> + +<img src="/img/pbr/ibl_hemisphere_sample.png" class="clean" alt="Convoluting a cubemap on a hemisphere for a PBR irradiance map."/> + +<p> + This pre-computed cubemap, that for each sample direction \(w_o\) stores the integral result, can be thought of as the pre-computed sum of all indirect diffuse light of the scene hitting some surface aligned along direction \(w_o\). Such a cubemap is known as an <def>irradiance map</def> seeing as the convoluted cubemap effectively allows us to directly sample the scene's (pre-computed) irradiance from any direction \(w_o\). +</p> + +<note> + The radiance equation also depends on a position \(p\), which we've assumed to be at the center of the irradiance map. This does mean all diffuse indirect light must come from a single environment map which may break the illusion of reality (especially indoors). Render engines solve this by placing <def>reflection probes</def> all over the scene where each reflection probes calculates its own irradiance map of its surroundings. This way, the irradiance (and radiance) at position \(p\) is the interpolated irradiance between its closest reflection probes. For now, we assume we always sample the environment map from its center. +</note> + +<p> + Below is an example of a cubemap environment map and its resulting irradiance map (courtesy of <a href="http://www.indiedb.com/features/using-image-based-lighting-ibl" target="_blank">wave engine</a>), averaging the scene's radiance for every direction \(w_o\). +</p> + +<img src="/img/pbr/ibl_irradiance.png" class="clean" alt="The effect of convoluting a cubemap environment map."/> + +<p> + By storing the convoluted result in each cubemap texel (in the direction of \(w_o\)), the irradiance map displays somewhat like an average color or lighting display of the environment. Sampling any direction from this environment map will give us the scene's irradiance in that particular direction. +</p> + + +<h2>PBR and HDR</h2> +<p> + We've briefly touched upon it in the <a href="https://learnopengl.com/PBR/Lighting" target="_blank">previous</a> chapter: taking the high dynamic range of your scene's lighting into account in a PBR pipeline is incredibly important. As PBR bases most of its inputs on real physical properties and measurements it makes sense to closely match the incoming light values to their physical equivalents. Whether we make educated guesses on each light's radiant flux or use their <a href="https://en.wikipedia.org/wiki/Lumen_(unit)" target="_blank">direct physical equivalent</a>, the difference between a simple light bulb or the sun is significant either way. Without working in an <a href="https://learnopengl.com/Advanced-Lighting/HDR" target="_blank">HDR</a> render environment it's impossible to correctly specify each light's relative intensity. +</p> + +<p> + So, PBR and HDR go hand in hand, but how does it all relate to image based lighting? We've seen in the previous chapter that it's relatively easy to get PBR working in HDR. However, seeing as for image based lighting we base the environment's indirect light intensity on the color values of an environment cubemap we need some way to store the lighting's high dynamic range into an environment map. +</p> + +<p> + The environment maps we've been using so far as cubemaps (used as <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps" target="_blank">skyboxes</a> for instance) are in low dynamic range (LDR). We directly used their color values from the individual face images, ranged between <code>0.0</code> and <code>1.0</code>, and processed them as is. While this may work fine for visual output, when taking them as physical input parameters it's not going to work. +</p> + +<h3>The radiance HDR file format</h3> +<p> + Enter the radiance file format. The radiance file format (with the <code>.hdr</code> extension) stores a full cubemap with all 6 faces as floating point data. This allows us to specify color values outside the <code>0.0</code> to <code>1.0</code> range to give lights their correct color intensities. The file format also uses a clever trick to store each floating point value, not as a 32 bit value per channel, but 8 bits per channel using the color's alpha channel as an exponent (this does come with a loss of precision). This works quite well, but requires the parsing program to re-convert each color to their floating point equivalent. +</p> + +<p> + There are quite a few radiance HDR environment maps freely available from sources like <a href="http://www.hdrlabs.com/sibl/archive.html" target="_blank">sIBL archive</a> of which you can see an example below: +</p> + +<img src="/img/pbr/ibl_hdr_radiance.png" alt="Example of an equirectangular map"/> + +<p> + This may not be exactly what you were expecting, as the image appears distorted and doesn't show any of the 6 individual cubemap faces of environment maps we've seen before. This environment map is projected from a sphere onto a flat plane such that we can more easily store the environment into a single image known as an <def>equirectangular map</def>. This does come with a small caveat as most of the visual resolution is stored in the horizontal view direction, while less is preserved in the bottom and top directions. In most cases this is a decent compromise as with almost any renderer you'll find most of the interesting lighting and surroundings in the horizontal viewing directions. +</p> + +<h3>HDR and stb_image.h</h3> +<p> + Loading radiance HDR images directly requires some knowledge of the <a href="http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html" target="_blank">file format</a> which isn't too difficult, but cumbersome nonetheless. Lucky for us, the popular one header library <a href="https://github.com/nothings/stb/blob/master/stb_image.h" target="_blank">stb_image.h</a> supports loading radiance HDR images directly as an array of floating point values which perfectly fits our needs. With <code>stb_image</code> added to your project, loading an HDR image is now as simple as follows: +</p> + +<pre><code> +#include "stb_image.h" +[...] + +stbi_set_flip_vertically_on_load(true); +int width, height, nrComponents; +float *data = stbi_loadf("newport_loft.hdr", &width, &height, &nrComponents, 0); +unsigned int hdrTexture; +if (data) +{ + <function id='50'>glGenTextures</function>(1, &hdrTexture); + <function id='48'>glBindTexture</function>(GL_TEXTURE_2D, hdrTexture); + <function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data); + + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + <function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + stbi_image_free(data); +} +else +{ + std::cout &lt;&lt; "Failed to load HDR image." &lt;&lt; std::endl; +} +</code></pre> + +<p> + <code>stb_image.h</code> automatically maps the HDR values to a list of floating point values: 32 bits per channel and 3 channels per color by default. This is all we need to store the equirectangular HDR environment map into a 2D floating point texture. +</p> + +<h3>From Equirectangular to Cubemap</h3> +<p> + It is possible to use the equirectangular map directly for environment lookups, but these operations can be relatively expensive in which case a direct cubemap sample is more performant. Therefore, in this chapter we'll first convert the equirectangular image to a cubemap for further processing. Note that in the process we also show how to sample an equirectangular map as if it was a 3D environment map in which case you're free to pick whichever solution you prefer. +</p> + +<p> + To convert an equirectangular image into a cubemap we need to render a (unit) cube and project the equirectangular map on all of the cube's faces from the inside and take 6 images of each of the cube's sides as a cubemap face. The vertex shader of this cube simply renders the cube as is and passes its local position to the fragment shader as a 3D sample vector: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +out vec3 localPos; + +uniform mat4 projection; +uniform mat4 view; + +void main() +{ + localPos = aPos; + gl_Position = projection * view * vec4(localPos, 1.0); +} +</code></pre> + +<p> + For the fragment shader, we color each part of the cube as if we neatly folded the equirectangular map onto each side of the cube. To accomplish this, we take the fragment's sample direction as interpolated from the cube's local position and then use this direction vector and some trigonometry magic (spherical to cartesian) to sample the equirectangular map as if it's a cubemap itself. We directly store the result onto the cube-face's fragment which should be all we need to do: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec3 localPos; + +uniform sampler2D equirectangularMap; + +const vec2 invAtan = vec2(0.1591, 0.3183); +vec2 SampleSphericalMap(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= invAtan; + uv += 0.5; + return uv; +} + +void main() +{ + vec2 uv = SampleSphericalMap(normalize(localPos)); // make sure to normalize localPos + vec3 color = texture(equirectangularMap, uv).rgb; + + FragColor = vec4(color, 1.0); +} + +</code></pre> + +<p> + If you render a cube at the center of the scene given an HDR equirectangular map you'll get something that looks like this: +</p> + + <img src="/img/pbr/ibl_equirectangular_projection.png" alt="OpenGL render of an equirectangular map converted to a cubemap."/> + +<p> + This demonstrates that we effectively mapped an equirectangular image onto a cubic shape, but doesn't yet help us in converting the source HDR image to a cubemap texture. To accomplish this we have to render the same cube 6 times, looking at each individual face of the cube, while recording its visual result with a <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffer</a> object: +</p> + +<pre><code> +unsigned int captureFBO, captureRBO; +<function id='76'>glGenFramebuffers</function>(1, &captureFBO); +<function id='82'>glGenRenderbuffers</function>(1, &captureRBO); + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, captureRBO); +<function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); +<function id='89'>glFramebufferRenderbuffer</function>(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO); +</code></pre> + +<p> + Of course, we then also generate the corresponding cubemap color textures, pre-allocating memory for each of its 6 faces: +</p> + +<pre><code> +unsigned int envCubemap; +<function id='50'>glGenTextures</function>(1, &envCubemap); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, envCubemap); +for (unsigned int i = 0; i &lt; 6; ++i) +{ + // note that we store each face with 16 bit floating point values + <function id='52'>glTexImage2D</function>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, + 512, 512, 0, GL_RGB, GL_FLOAT, nullptr); +} +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<p> + Then what's left to do is capture the equirectangular 2D texture onto the cubemap faces. +</p> + +<p> + I won't go over the details as the code details topics previously discussed in the <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffer</a> and <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows" target="_blank">point shadows</a> chapters, but it effectively boils down to setting up 6 different view matrices (facing each side of the cube), set up a projection matrix with a fov of <code>90</code> degrees to capture the entire face, and render a cube 6 times storing the results in a floating point framebuffer: +</p> + +<pre><code> +glm::mat4 captureProjection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(90.0f), 1.0f, 0.1f, 10.0f); +glm::mat4 captureViews[] = +{ + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)), + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)), + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)), + <function id='62'>glm::lookAt</function>(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)) +}; + +// convert HDR equirectangular environment map to cubemap equivalent +equirectangularToCubemapShader.use(); +equirectangularToCubemapShader.setInt("equirectangularMap", 0); +equirectangularToCubemapShader.setMat4("projection", captureProjection); +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, hdrTexture); + +<function id='22'>glViewport</function>(0, 0, 512, 512); // don't forget to configure the viewport to the capture dimensions. +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +for (unsigned int i = 0; i &lt; 6; ++i) +{ + equirectangularToCubemapShader.setMat4("view", captureViews[i]); + <function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, 0); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + renderCube(); // renders a 1x1 cube +} +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + We take the color attachment of the framebuffer and switch its texture target around for every face of the cubemap, directly rendering the scene into one of the cubemap's faces. Once this routine has finished (which we only have to do once), the cubemap <var>envCubemap</var> should be the cubemapped environment version of our original HDR image. +</p> + +<p> + Let's test the cubemap by writing a very simple skybox shader to display the cubemap around us: +</p> + +<pre><code> +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 projection; +uniform mat4 view; + +out vec3 localPos; + +void main() +{ + localPos = aPos; + + mat4 rotView = mat4(mat3(view)); // remove translation from the view matrix + vec4 clipPos = projection * rotView * vec4(localPos, 1.0); + + gl_Position = clipPos.xyww; +} +</code></pre> + +<p> + Note the <code>xyww</code> trick here that ensures the depth value of the rendered cube fragments always end up at <code>1.0</code>, the maximum depth value, as described in the <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps" target="_blank">cubemap</a> chapter. Do note that we need to change the depth comparison function to <var>GL_LEQUAL</var>: +</p> + +<pre><code> +<function id='66'>glDepthFunc</function>(GL_LEQUAL); +</code></pre> + +<p> + The fragment shader then directly samples the cubemap environment map using the cube's local fragment position: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; + +in vec3 localPos; + +uniform samplerCube environmentMap; + +void main() +{ + vec3 envColor = texture(environmentMap, localPos).rgb; + + envColor = envColor / (envColor + vec3(1.0)); + envColor = pow(envColor, vec3(1.0/2.2)); + + FragColor = vec4(envColor, 1.0); +} +</code></pre> + +<p> + We sample the environment map using its interpolated vertex cube positions that directly correspond to the correct direction vector to sample. Seeing as the camera's translation components are ignored, rendering this shader over a cube should give you the environment map as a non-moving background. Also, as we directly output the environment map's HDR values to the default LDR framebuffer, we want to properly tone map the color values. Furthermore, almost all HDR maps are in linear color space by default so we need to apply <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction" target="_blank">gamma correction</a> before writing to the default framebuffer. +</p> + +<p> + Now rendering the sampled environment map over the previously rendered spheres should look something like this: +</p> + + <img src="/img/pbr/ibl_hdr_environment_mapped.png" alt="Render the converted cubemap as a skybox."/> + +<p> + Well... it took us quite a bit of setup to get here, but we successfully managed to read an HDR environment map, convert it from its equirectangular mapping to a cubemap, and render the HDR cubemap into the scene as a skybox. Furthermore, we set up a small system to render onto all 6 faces of a cubemap, which we'll need again when <def>convoluting</def> the environment map. You can find the source code of the entire conversion process <a href="/code_viewer_gh.php?code=src/6.pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion.cpp" target="_blank">here</a>. +</p> + +<h2>Cubemap convolution</h2> +<p> + As described at the start of the chapter, our main goal is to solve the integral for all diffuse indirect lighting given the scene's irradiance in the form of a cubemap environment map. We know that we can get the radiance of the scene \(L(p, w_i)\) in a particular direction by sampling an HDR environment map in direction \(w_i\). To solve the integral, we have to sample the scene's radiance from all possible directions within the hemisphere \(\Omega\) for each fragment. + </p> + +<p> + It is however computationally impossible to sample the environment's lighting from every possible direction in \(\Omega\), the number of possible directions is theoretically infinite. We can however, approximate the number of directions by taking a finite number of directions or samples, spaced uniformly or taken randomly from within the hemisphere, to get a fairly accurate approximation of the irradiance; effectively solving the integral \(\int\) discretely +</p> + +<p> + It is however still too expensive to do this for every fragment in real-time as the number of samples needs to be significantly large for decent results, so we want to <def>pre-compute</def> this. Since the orientation of the hemisphere decides where we capture the irradiance, we can pre-calculate the irradiance for every possible hemisphere orientation oriented around all outgoing directions \(w_o\): +</p> + +\[ + L_o(p,\omega_o) = + k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + Given any direction vector \(w_i\) in the lighting pass, we can then sample the pre-computed irradiance map to retrieve the total diffuse irradiance from direction \(w_i\). To determine the amount of indirect diffuse (irradiant) light at a fragment surface, we retrieve the total irradiance from the hemisphere oriented around its surface normal. Obtaining the scene's irradiance is then as simple as: +</p> + +<pre><code> +vec3 irradiance = texture(irradianceMap, N).rgb; +</code></pre> + +<p> + Now, to generate the irradiance map, we need to convolute the environment's lighting as converted to a cubemap. Given that for each fragment the surface's hemisphere is oriented along the normal vector \(N\), convoluting a cubemap equals calculating the total averaged radiance of each direction \(w_i\) in the hemisphere \(\Omega\) oriented along \(N\). +</p> + +<img src="/img/pbr/ibl_hemisphere_sample_normal.png" class="clean" alt="Convoluting a cubemap on a hemisphere (oriented around the normal) for a PBR irradiance map."/> + +<p> + Thankfully, all of the cumbersome setup of this chapter isn't all for nothing as we can now directly take the converted cubemap, convolute it in a fragment shader, and capture its result in a new cubemap using a framebuffer that renders to all 6 face directions. As we've already set this up for converting the equirectangular environment map to a cubemap, we can take the exact same approach but use a different fragment shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec3 localPos; + +uniform samplerCube environmentMap; + +const float PI = 3.14159265359; + +void main() +{ + // the sample direction equals the hemisphere's orientation + vec3 normal = normalize(localPos); + + vec3 irradiance = vec3(0.0); + + [...] // convolution code + + FragColor = vec4(irradiance, 1.0); +} +</code></pre> + +<p> + With <var>environmentMap</var> being the HDR cubemap as converted from the equirectangular HDR environment map. +</p> + +<p> + There are many ways to convolute the environment map, but for this chapter we're going to generate a fixed amount of sample vectors for each cubemap texel along a hemisphere \(\Omega\) oriented around the sample direction and average the results. The fixed amount of sample vectors will be uniformly spread inside the hemisphere. Note that an integral is a continuous function and discretely sampling its function given a fixed amount of sample vectors will be an approximation. The more sample vectors we use, the better we approximate the integral. +</p> + +<p> + The integral \(\int\) of the reflectance equation revolves around the solid angle \(dw\) which is rather difficult to work with. Instead of integrating over the solid angle \(dw\) we'll integrate over its equivalent spherical coordinates \(\theta\) and \(\phi\). +</p> + + + <img src="/img/pbr/ibl_spherical_integrate.png" class="clean" alt="Converting the solid angle over the equivalent polar azimuth and inclination angle for PBR"/> + +<p> + We use the polar azimuth \(\phi\) angle to sample around the ring of the hemisphere between \(0\) and \(2\pi\), and use the inclination zenith \(\theta\) angle between \(0\) and \(\frac{1}{2}\pi\) to sample the increasing rings of the hemisphere. This will give us the updated reflectance integral: +</p> + +\[ + L_o(p,\phi_o, \theta_o) = + k_d\frac{c}{\pi} \int_{\phi = 0}^{2\pi} \int_{\theta = 0}^{\frac{1}{2}\pi} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta +\] + +<p> + Solving the integral requires us to take a fixed number of discrete samples within the hemisphere \(\Omega\) and averaging their results. This translates the integral to the following discrete version as based on the <a href="https://en.wikipedia.org/wiki/Riemann_sum" target="_blank">Riemann sum</a> given \(n1\) and \(n2\) discrete samples on each spherical coordinate respectively: +</p> + +\[ + L_o(p,\phi_o, \theta_o) = + k_d \frac{c\pi}{n1 n2} \sum_{\phi = 0}^{n1} \sum_{\theta = 0}^{n2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta +\] + + +<p> + As we sample both spherical values discretely, each sample will approximate or average an area on the hemisphere as the image before shows. Note that (due to the general properties of a spherical shape) the hemisphere's discrete sample area gets smaller the higher the zenith angle \(\theta\) as the sample regions converge towards the center top. To compensate for the smaller areas, we weigh its contribution by scaling the area by \(\sin \theta\). +</p> + +<p> + Discretely sampling the hemisphere given the integral's spherical coordinates translates to the following fragment code: +</p> + +<pre><code> +vec3 irradiance = vec3(0.0); + +vec3 up = vec3(0.0, 1.0, 0.0); +vec3 right = normalize(cross(up, normal)); +up = normalize(cross(normal, right)); + +float sampleDelta = 0.025; +float nrSamples = 0.0; +for(float phi = 0.0; phi &lt; 2.0 * PI; phi += sampleDelta) +{ + for(float theta = 0.0; theta &lt; 0.5 * PI; theta += sampleDelta) + { + // spherical to cartesian (in tangent space) + vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); + // tangent space to world + vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; + + irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta); + nrSamples++; + } +} +irradiance = PI * irradiance * (1.0 / float(nrSamples)); +</code></pre> + +<p> + We specify a fixed <var>sampleDelta</var> delta value to traverse the hemisphere; decreasing or increasing the sample delta will increase or decrease the accuracy respectively. +</p> + +<p> + From within both loops, we take both spherical coordinates to convert them to a 3D Cartesian sample vector, convert the sample from tangent to world space oriented around the normal, and use this sample vector to directly sample the HDR environment map. We add each sample result to <var>irradiance</var> which at the end we divide by the total number of samples taken, giving us the average sampled irradiance. Note that we scale the sampled color value by <code>cos(theta)</code> due to the light being weaker at larger angles and by <code>sin(theta)</code> to account for the smaller sample areas in the higher hemisphere areas. +</p> + +<p> + Now what's left to do is to set up the OpenGL rendering code such that we can convolute the earlier captured <var>envCubemap</var>. First we create the irradiance cubemap (again, we only have to do this once before the render loop): +</p> + +<pre><code> +unsigned int irradianceMap; +<function id='50'>glGenTextures</function>(1, &irradianceMap); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, irradianceMap); +for (unsigned int i = 0; i &lt; 6; ++i) +{ + <function id='52'>glTexImage2D</function>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, + GL_RGB, GL_FLOAT, nullptr); +} +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<p> + As the irradiance map averages all surrounding radiance uniformly it doesn't have a lot of high frequency details, so we can store the map at a low resolution (32x32) and let OpenGL's linear filtering do most of the work. Next, we re-scale the capture framebuffer to the new resolution: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, captureRBO); +<function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32); +</code></pre> + +<p> + Using the convolution shader, we render the environment map in a similar way to how we captured the environment cubemap: +</p> + +<pre><code> +irradianceShader.use(); +irradianceShader.setInt("environmentMap", 0); +irradianceShader.setMat4("projection", captureProjection); +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, envCubemap); + +<function id='22'>glViewport</function>(0, 0, 32, 32); // don't forget to configure the viewport to the capture dimensions. +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +for (unsigned int i = 0; i &lt; 6; ++i) +{ + irradianceShader.setMat4("view", captureViews[i]); + <function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceMap, 0); + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + renderCube(); +} +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + Now after this routine we should have a pre-computed irradiance map that we can directly use for our diffuse image based lighting. To see if we successfully convoluted the environment map we'll substitute the environment map for the irradiance map as the skybox's environment sampler: +</p> + + <img src="/img/pbr/ibl_irradiance_map_background.png" alt="Displaying a PBR irradiance map as the skybox background."/> + +<p> + If it looks like a heavily blurred version of the environment map you've successfully convoluted the environment map. +</p> + +<h2>PBR and indirect irradiance lighting</h2> +<p> + The irradiance map represents the diffuse part of the reflectance integral as accumulated from all surrounding indirect light. Seeing as the light doesn't come from direct light sources, but from the surrounding environment, we treat both the diffuse and specular indirect lighting as the ambient lighting, replacing our previously set constant term. +</p> + +<p> + First, be sure to add the pre-calculated irradiance map as a cube sampler: +</p> + +<pre><code> +uniform samplerCube irradianceMap; +</code></pre> + +<p> + Given the irradiance map that holds all of the scene's indirect diffuse light, retrieving the irradiance influencing the fragment is as simple as a single texture sample given the surface normal: +</p> + +<pre><code> +// vec3 ambient = vec3(0.03); +vec3 ambient = texture(irradianceMap, N).rgb; +</code></pre> + +<p> + However, as the indirect lighting contains both a diffuse and specular part (as we've seen from the split version of the reflectance equation) we need to weigh the diffuse part accordingly. Similar to what we did in the previous chapter, we use the Fresnel equation to determine the surface's indirect reflectance ratio from which we derive the refractive (or diffuse) ratio: +</p> + +<pre><code> +vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0); +vec3 kD = 1.0 - kS; +vec3 irradiance = texture(irradianceMap, N).rgb; +vec3 diffuse = irradiance * albedo; +vec3 ambient = (kD * diffuse) * ao; +</code></pre> + +<p> + As the ambient light comes from all directions within the hemisphere oriented around the normal <var>N</var>, there's no single halfway vector to determine the Fresnel response. To still simulate Fresnel, we calculate the Fresnel from the angle between the normal and view vector. However, earlier we used the micro-surface halfway vector, influenced by the roughness of the surface, as input to the Fresnel equation. As we currently don't take roughness into account, the surface's reflective ratio will always end up relatively high. Indirect light follows the same properties of direct light so we expect rougher surfaces to reflect less strongly on the surface edges. Because of this, the indirect Fresnel reflection strength looks off on rough non-metal surfaces (slightly exaggerated for demonstration purposes): +</p> + <img src="/img/pbr/lighting_fresnel_no_roughness.png" alt="The Fresnel equation for IBL without taking roughness into account."/> + +<p> + We can alleviate the issue by injecting a roughness term in the Fresnel-Schlick equation as described by <a href="https://seblagarde.wordpress.com/2011/08/17/hello-world/" target="_blank">Sébastien Lagarde</a>: +</p> + +<pre><code> +vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); +} +</code></pre> + +<p> + By taking account of the surface's roughness when calculating the Fresnel response, the ambient code ends up as: +</p> + +<pre><code> +vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); +vec3 kD = 1.0 - kS; +vec3 irradiance = texture(irradianceMap, N).rgb; +vec3 diffuse = irradiance * albedo; +vec3 ambient = (kD * diffuse) * ao; +</code></pre> + +<p> + As you can see, the actual image based lighting computation is quite simple and only requires a single cubemap texture lookup; most of the work is in pre-computing or convoluting the irradiance map. +</p> + +<p> + If we take the initial scene from the PBR <a href="https://learnopengl.com/PBR/Lighting" target="_blank">lighting</a> chapter, where each sphere has a vertically increasing metallic and a horizontally increasing roughness value, and add the diffuse image based lighting it'll look a bit like this: +</p> + + <img src="/img/pbr/ibl_irradiance_result.png" alt="Result of convoluting an irradiance map in OpenGL used by the PBR shader."/> + +<p> + It still looks a bit weird as the more metallic spheres <strong>require</strong> some form of reflection to properly start looking like metallic surfaces (as metallic surfaces don't reflect diffuse light) which at the moment are only (barely) coming from the point light sources. Nevertheless, you can already tell the spheres do feel more <em>in place</em> within the environment (especially if you switch between environment maps) as the surface response reacts accordingly to the environment's ambient lighting. +</p> + +<p> + You can find the complete source code of the discussed topics <a href="/code_viewer_gh.php?code=src/6.pbr/2.1.2.ibl_irradiance/ibl_irradiance.cpp" target="_blank">here</a>. In the <a href="https://learnopengl.com/PBR/IBL/Specular-IBL" target="_blank">next</a> chapter we'll add the indirect specular part of the reflectance integral at which point we're really going to see the power of PBR. +</p> + +<h2>Further reading</h2> +<ul> + <li><a href="http://www.codinglabs.net/article_physically_based_rendering.aspx" target="_blank">Coding Labs: Physically based rendering</a>: an introduction to PBR and how and why to generate an irradiance map. </li> + <li><a href="http://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/mathematics-of-shading" target="_blank">The Mathematics of Shading</a>: a brief introduction by ScratchAPixel on several of the mathematics described in this tutorial, specifically on polar coordinates and integrals.</li> +</ul> + + </div> + + </main> +</body> +</html> diff --git a/pub/PBR/IBL/Specular-IBL.html b/pub/PBR/IBL/Specular-IBL.html @@ -0,0 +1,1174 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Specular IBL</h1> +<h1 id="content-url" style='display:none;'>PBR/IBL/Specular-IBL</h1> +<p> + In the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">previous</a> chapter we've set up PBR in combination with image based lighting by pre-computing an irradiance map as the lighting's indirect diffuse portion. In this chapter we'll focus on the specular part of the reflectance equation: +</p> + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + You'll notice that the Cook-Torrance specular portion (multiplied by \(kS\)) isn't constant over the integral and is dependent on the incoming light direction, but <strong>also</strong> the incoming view direction. Trying to solve the integral for all incoming light directions including all possible view directions is a combinatorial overload and way too expensive to calculate on a real-time basis. Epic Games proposed a solution where they were able to pre-convolute the specular part for real time purposes, given a few compromises, known as the <def>split sum approximation</def>. +</p> + +<p> + The split sum approximation splits the specular part of the reflectance equation into two separate parts that we can individually convolute and later combine in the PBR shader for specular indirect image based lighting. Similar to how we pre-convoluted the irradiance map, the split sum approximation requires an HDR environment map as its convolution input. To understand the split sum approximation we'll again look at the reflectance equation, but this time focus on the specular part: +</p> + +\[ + L_o(p,\omega_o) = + \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} + L_i(p,\omega_i) n \cdot \omega_i d\omega_i + = + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i +\] + +<p> + For the same (performance) reasons as the irradiance convolution, we can't solve the specular part of the integral in real time and expect a reasonable performance. So preferably we'd pre-compute this integral to get something like a specular IBL map, sample this map with the fragment's normal, and be done with it. However, this is where it gets a bit tricky. We were able to pre-compute the irradiance map as the integral only depended on \(\omega_i\) and we could move the constant diffuse albedo terms out of the integral. This time, the integral depends on more than just \(\omega_i\) as evident from the BRDF: +</p> + +\[ + f_r(p, w_i, w_o) = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} +\] + +<p> + The integral also depends on \(w_o\), and we can't really sample a pre-computed cubemap with two direction vectors. The position \(p\) is irrelevant here as described in the previous chapter. Pre-computing this integral for every possible combination of \(\omega_i\) and \(\omega_o\) isn't practical in a real-time setting. +</p> + +<p> + Epic Games' split sum approximation solves the issue by splitting the pre-computation into 2 individual parts that we can later combine to get the resulting pre-computed result we're after. The split sum approximation splits the specular integral into two separate integrals: +</p> + +\[ + L_o(p,\omega_o) = + \int\limits_{\Omega} L_i(p,\omega_i) d\omega_i + * + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i +\] + +<p> + The first part (when convoluted) is known as the <def>pre-filtered environment map</def> which is (similar to the irradiance map) a pre-computed environment convolution map, but this time taking roughness into account. For increasing roughness levels, the environment map is convoluted with more scattered sample vectors, creating blurrier reflections. For each roughness level we convolute, we store the sequentially blurrier results in the pre-filtered map's mipmap levels. For instance, a pre-filtered environment map storing the pre-convoluted result of 5 different roughness values in its 5 mipmap levels looks as follows: +</p> + +<img src="/img/pbr/ibl_prefilter_map.png" class="clean" alt="Pre-convoluted environment map over 5 roughness levels for PBR"/> + + + <p> + We generate the sample vectors and their scattering amount using the normal distribution function (NDF) of the Cook-Torrance BRDF that takes as input both a normal and view direction. As we don't know beforehand the view direction when convoluting the environment map, Epic Games makes a further approximation by assuming the view direction (and thus the specular reflection direction) to be equal to the output sample direction \(\omega_o\). This translates itself to the following code: +</p> + +<pre><code> +vec3 N = normalize(w_o); +vec3 R = N; +vec3 V = R; +</code></pre> + +<p> + This way, the pre-filtered environment convolution doesn't need to be aware of the view direction. This does mean we don't get nice grazing specular reflections when looking at specular surface reflections from an angle as seen in the image below (courtesy of the <em>Moving Frostbite to PBR</em> article); this is however generally considered an acceptable compromise: +</p> + +<img src="/img/pbr/ibl_grazing_angles.png" class="clean" alt="Removing grazing specular reflections with the split sum approximation of V = R = N."/> + +<p> + The second part of the split sum equation equals the BRDF part of the specular integral. If we pretend the incoming radiance is completely white for every direction (thus \(L(p, x) = 1.0\)) we can pre-calculate the BRDF's response given an input roughness and an input angle between the normal \(n\) and light direction \(\omega_i\), or \(n \cdot \omega_i\). Epic Games stores the pre-computed BRDF's response to each normal and light direction combination on varying roughness values in a 2D lookup texture (LUT) known as the <def>BRDF integration</def> map. The 2D lookup texture outputs a scale (red) and a bias value (green) to the surface's Fresnel response giving us the second part of the split specular integral: +</p> + + <img src="/img/pbr/ibl_brdf_lut.png" alt="Visualization of the 2D BRDF LUT according to the split sum approximation for PBR in OpenGL."/> + +<p> + We generate the lookup texture by treating the horizontal texture coordinate (ranged between <code>0.0</code> and <code>1.0</code>) of a plane as the BRDF's input \(n \cdot \omega_i\), and its vertical texture coordinate as the input roughness value. With this BRDF integration map and the pre-filtered environment map we can combine both to get the result of the specular integral: +</p> + +<pre><code> +float lod = getMipLevelFromRoughness(roughness); +vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod); +vec2 envBRDF = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy; +vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y) +</code></pre> + +<p> + This should give you a bit of an overview on how Epic Games' split sum approximation roughly approaches the indirect specular part of the reflectance equation. Let's now try and build the pre-convoluted parts ourselves. +</p> + +<h2>Pre-filtering an HDR environment map</h2> +<p> + Pre-filtering an environment map is quite similar to how we convoluted an irradiance map. The difference being that we now account for roughness and store sequentially rougher reflections in the pre-filtered map's mip levels. +</p> + +<p> + First, we need to generate a new cubemap to hold the pre-filtered environment map data. To make sure we allocate enough memory for its mip levels we call <fun><function id='51'>glGenerateMipmap</function></fun> as an easy way to allocate the required amount of memory: +</p> + +<pre><code> +unsigned int prefilterMap; +<function id='50'>glGenTextures</function>(1, &prefilterMap); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, prefilterMap); +for (unsigned int i = 0; i &lt; 6; ++i) +{ + <function id='52'>glTexImage2D</function>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 128, 128, 0, GL_RGB, GL_FLOAT, nullptr); +} +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +<function id='51'>glGenerateMipmap</function>(GL_TEXTURE_CUBE_MAP); +</code></pre> + +<p> + Note that because we plan to sample <var>prefilterMap</var>'s mipmaps you'll need to make sure its minification filter is set to <var>GL_LINEAR_MIPMAP_LINEAR</var> to enable trilinear filtering. We store the pre-filtered specular reflections in a per-face resolution of 128 by 128 at its base mip level. This is likely to be enough for most reflections, but if you have a large number of smooth materials (think of car reflections) you may want to increase the resolution. +</p> + +<p> + In the previous chapter we convoluted the environment map by generating sample vectors uniformly spread over the hemisphere \(\Omega\) using spherical coordinates. While this works just fine for irradiance, for specular reflections it's less efficient. When it comes to specular reflections, based on the roughness of a surface, the light reflects closely or roughly around a reflection vector \(r\) over a normal \(n\), but (unless the surface is extremely rough) around the reflection vector nonetheless: +</p> + + <img src="/img/pbr/ibl_specular_lobe.png" class="clean" alt="Specular lobe according to the PBR microfacet surface model."/> + +<p> + The general shape of possible outgoing light reflections is known as the <def>specular lobe</def>. As roughness increases, the specular lobe's size increases; and the shape of the specular lobe changes on varying incoming light directions. The shape of the specular lobe is thus highly dependent on the material. +</p> + +<p> + When it comes to the microsurface model, we can imagine the specular lobe as the reflection orientation about the microfacet halfway vectors given some incoming light direction. Seeing as most light rays end up in a specular lobe reflected around the microfacet halfway vectors, it makes sense to generate the sample vectors in a similar fashion as most would otherwise be wasted. This process is known as <def>importance sampling</def>. +</p> + +<h3>Monte Carlo integration and importance sampling</h3> +<p> + To fully get a grasp of importance sampling it's relevant we first delve into the mathematical construct known as <def>Monte Carlo integration</def>. Monte Carlo integration revolves mostly around a combination of statistics and probability theory. Monte Carlo helps us in discretely solving the problem of figuring out some statistic or value of a population without having to take <strong>all</strong> of the population into consideration. +</p> + +<p> + For instance, let's say you want to count the average height of all citizens of a country. To get your result, you could measure <strong>every</strong> citizen and average their height which will give you the <strong>exact</strong> answer you're looking for. However, since most countries have a considerable population this isn't a realistic approach: it would take too much effort and time. +</p> + +<p> + A different approach is to pick a much smaller <strong>completely random</strong> (unbiased) subset of this population, measure their height, and average the result. This population could be as small as a 100 people. While not as accurate as the exact answer, you'll get an answer that is relatively close to the ground truth. This is known as the <def>law of large numbers</def>. The idea is that if you measure a smaller set of size \(N\) of truly random samples from the total population, the result will be relatively close to the true answer and gets closer as the number of samples \(N\) increases. +</p> + +<p> + Monte Carlo integration builds on this law of large numbers and takes the same approach in solving an integral. Rather than solving an integral for all possible (theoretically infinite) sample values \(x\), simply generate \(N\) sample values randomly picked from the total population and average. As \(N\) increases, we're guaranteed to get a result closer to the exact answer of the integral: +</p> + +\[ + O = \int\limits_{a}^{b} f(x) dx + = + \frac{1}{N} \sum_{i=0}^{N-1} \frac{f(x)}{pdf(x)} +\] + +<p> + To solve the integral, we take \(N\) random samples over the population \(a\) to \(b\), add them together, and divide by the total number of samples to average them. The \(pdf\) stands for the <def>probability density function</def> that tells us the probability a specific sample occurs over the total sample set. For instance, the pdf of the height of a population would look a bit like this: +</p> + +<img src="/img/pbr/ibl_pdf.png" class="clean" alt="Example PDF (probability distribution function)."/> + +<p> + From this graph we can see that if we take any random sample of the population, there is a higher chance of picking a sample of someone of height 1.70, compared to the lower probability of the sample being of height 1.50. + </p> + +<p> +When it comes to Monte Carlo integration, some samples may have a higher probability of being generated than others. This is why for any general Monte Carlo estimation we divide or multiply the sampled value by the sample probability according to a pdf. So far, in each of our cases of estimating an integral, the samples we've generated were uniform, having the exact same chance of being generated. Our estimations so far were <def>unbiased</def>, meaning that given an ever-increasing amount of samples we will eventually <def>converge</def> to the <strong>exact</strong> solution of the integral. +</p> + +<p> + However, some Monte Carlo estimators are <def>biased</def>, meaning that the generated samples aren't completely random, but focused towards a specific value or direction. These biased Monte Carlo estimators have a <def>faster rate of convergence</def>, meaning they can converge to the exact solution at a much faster rate, but due to their biased nature it's likely they won't ever converge to the exact solution. This is generally an acceptable tradeoff, especially in computer graphics, as the exact solution isn't too important as long as the results are visually acceptable. + As we'll soon see with importance sampling (which uses a biased estimator), the generated samples are biased towards specific directions in which case we account for this by multiplying or dividing each sample by its corresponding pdf. + </p> + +<p> + Monte Carlo integration is quite prevalent in computer graphics as it's a fairly intuitive way to approximate continuous integrals in a discrete and efficient fashion: take any area/volume to sample over (like the hemisphere \(\Omega\)), generate \(N\) amount of random samples within the area/volume, and sum and weigh every sample contribution to the final result. +</p> + +<p> + Monte Carlo integration is an extensive mathematical topic and I won't delve much further into the specifics, but we'll mention that there are multiple ways of generating the <em>random samples</em>. By default, each sample is completely (pseudo)random as we're used to, but by utilizing certain properties of semi-random sequences we can generate sample vectors that are still random, but have interesting properties. For instance, we can do Monte Carlo integration on something called <def>low-discrepancy sequences</def> which still generate random samples, but each sample is more evenly distributed (image courtesy of James Heald): +</p> + + <img src="/img/pbr/ibl_low_discrepancy_sequence.png" class="clean" alt="Low discrepancy sequence."/> + +<p> + When using a low-discrepancy sequence for generating the Monte Carlo sample vectors, the process is known as <def>Quasi-Monte Carlo integration</def>. Quasi-Monte Carlo methods have a faster <def>rate of convergence</def> which makes them interesting for performance heavy applications. +</p> + +<p> + Given our newly obtained knowledge of Monte Carlo and Quasi-Monte Carlo integration, there is an interesting property we can use for an even faster rate of convergence known as <def>importance sampling</def>. We've mentioned it before in this chapter, but when it comes to specular reflections of light, the reflected light vectors are constrained in a specular lobe with its size determined by the roughness of the surface. Seeing as any (quasi-)randomly generated sample outside the specular lobe isn't relevant to the specular integral it makes sense to focus the sample generation to within the specular lobe, at the cost of making the Monte Carlo estimator biased. +</p> + +<p> + This is in essence what importance sampling is about: generate sample vectors in some region constrained by the roughness oriented around the microfacet's halfway vector. By combining Quasi-Monte Carlo sampling with a low-discrepancy sequence and biasing the sample vectors using importance sampling, we get a high rate of convergence. Because we reach the solution at a faster rate, we'll need significantly fewer samples to reach an approximation that is sufficient enough. +</p> + +<h3>A low-discrepancy sequence</h3> +<p> + In this chapter we'll pre-compute the specular portion of the indirect reflectance equation using importance sampling given a random low-discrepancy sequence based on the Quasi-Monte Carlo method. The sequence we'll be using is known as the <def>Hammersley Sequence</def> as carefully described by <a href="http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html" target="_blank">Holger Dammertz</a>. The Hammersley sequence is based on the <def>Van Der Corput</def> sequence which mirrors a decimal binary representation around its decimal point. +</p> + +<p> + Given some neat bit tricks, we can quite efficiently generate the Van Der Corput sequence in a shader program which we'll use to get a Hammersley sequence sample <var>i</var> over <code>N</code> total samples: +</p> + +<pre><code> +float RadicalInverse_VdC(uint bits) +{ + bits = (bits &lt;&lt; 16u) | (bits &gt;&gt; 16u); + bits = ((bits & 0x55555555u) &lt;&lt; 1u) | ((bits & 0xAAAAAAAAu) &gt;&gt; 1u); + bits = ((bits & 0x33333333u) &lt;&lt; 2u) | ((bits & 0xCCCCCCCCu) &gt;&gt; 2u); + bits = ((bits & 0x0F0F0F0Fu) &lt;&lt; 4u) | ((bits & 0xF0F0F0F0u) &gt;&gt; 4u); + bits = ((bits & 0x00FF00FFu) &lt;&lt; 8u) | ((bits & 0xFF00FF00u) &gt;&gt; 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} +// ---------------------------------------------------------------------------- +vec2 Hammersley(uint i, uint N) +{ + return vec2(float(i)/float(N), RadicalInverse_VdC(i)); +} +</code></pre> + +<p> + The GLSL <fun>Hammersley</fun> function gives us the low-discrepancy sample <var>i</var> of the total sample set of size <var>N</var>. +</p> + +<note> +<strong>Hammersley sequence without bit operator support</strong><br/> +<p> + Not all OpenGL related drivers support bit operators (WebGL and OpenGL ES 2.0 for instance) in which case you may want to use an alternative version of the Van Der Corput Sequence that doesn't rely on bit operators: +</p> + +<pre><code> +float VanDerCorput(uint n, uint base) +{ + float invBase = 1.0 / float(base); + float denom = 1.0; + float result = 0.0; + + for(uint i = 0u; i &lt; 32u; ++i) + { + if(n > 0u) + { + denom = mod(float(n), 2.0); + result += denom * invBase; + invBase = invBase / 2.0; + n = uint(float(n) / 2.0); + } + } + + return result; +} +// ---------------------------------------------------------------------------- +vec2 HammersleyNoBitOps(uint i, uint N) +{ + return vec2(float(i)/float(N), VanDerCorput(i, 2u)); +} +</code></pre> + +<p> + Note that due to GLSL loop restrictions in older hardware, the sequence loops over all possible <code>32</code> bits. This version is less performant, but does work on all hardware if you ever find yourself without bit operators. +</p> +</note> + +<h3>GGX Importance sampling</h3> +<p> + Instead of uniformly or randomly (Monte Carlo) generating sample vectors over the integral's hemisphere \(\Omega\), we'll generate sample vectors biased towards the general reflection orientation of the microsurface halfway vector based on the surface's roughness. The sampling process will be similar to what we've seen before: begin a large loop, generate a random (low-discrepancy) sequence value, take the sequence value to generate a sample vector in tangent space, transform to world space, and sample the scene's radiance. What's different is that we now use a low-discrepancy sequence value as input to generate a sample vector: +</p> + +<pre><code> +const uint SAMPLE_COUNT = 4096u; +for(uint i = 0u; i &lt; SAMPLE_COUNT; ++i) +{ + vec2 Xi = Hammersley(i, SAMPLE_COUNT); +</code></pre> + +<p> + Additionally, to build a sample vector, we need some way of orienting and biasing the sample vector towards the specular lobe of some surface roughness. We can take the NDF as described in the <a href="https://learnopengl.com/PBR/Theory" target="_blank">theory</a> chapter and combine the GGX NDF in the spherical sample vector process as described by Epic Games: +</p> + +<pre><code> +vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) +{ + float a = roughness*roughness; + + float phi = 2.0 * PI * Xi.x; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + + // from spherical coordinates to cartesian coordinates + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = sin(phi) * sinTheta; + H.z = cosTheta; + + // from tangent-space vector to world-space sample vector + vec3 up = abs(N.z) &lt; 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + + vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; + return normalize(sampleVec); +} +</code></pre> + +<p> + This gives us a sample vector somewhat oriented around the expected microsurface's halfway vector based on some input roughness and the low-discrepancy sequence value <var>Xi</var>. Note that Epic Games uses the squared roughness for better visual results as based on Disney's original PBR research. +</p> + +<p> + With the low-discrepancy Hammersley sequence and sample generation defined, we can finalize the pre-filter convolution shader: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec3 localPos; + +uniform samplerCube environmentMap; +uniform float roughness; + +const float PI = 3.14159265359; + +float RadicalInverse_VdC(uint bits); +vec2 Hammersley(uint i, uint N); +vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); + +void main() +{ + vec3 N = normalize(localPos); + vec3 R = N; + vec3 V = R; + + const uint SAMPLE_COUNT = 1024u; + float totalWeight = 0.0; + vec3 prefilteredColor = vec3(0.0); + for(uint i = 0u; i &lt; SAMPLE_COUNT; ++i) + { + vec2 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, N, roughness); + vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(dot(N, L), 0.0); + if(NdotL > 0.0) + { + prefilteredColor += texture(environmentMap, L).rgb * NdotL; + totalWeight += NdotL; + } + } + prefilteredColor = prefilteredColor / totalWeight; + + FragColor = vec4(prefilteredColor, 1.0); +} + +</code></pre> + +<p> + We pre-filter the environment, based on some input roughness that varies over each mipmap level of the pre-filter cubemap (from <code>0.0</code> to <code>1.0</code>), and store the result in <var>prefilteredColor</var>. The resulting <var>prefilteredColor</var> is divided by the total sample weight, where samples with less influence on the final result (for small <var>NdotL</var>) contribute less to the final weight. +</p> + +<h3>Capturing pre-filter mipmap levels</h3> +<p> + What's left to do is let OpenGL pre-filter the environment map with different roughness values over multiple mipmap levels. This is actually fairly easy to do with the original setup of the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">irradiance</a> chapter: +</p> + +<pre><code> +prefilterShader.use(); +prefilterShader.setInt("environmentMap", 0); +prefilterShader.setMat4("projection", captureProjection); +<function id='49'>glActiveTexture</function>(GL_TEXTURE0); +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, envCubemap); + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +unsigned int maxMipLevels = 5; +for (unsigned int mip = 0; mip &lt; maxMipLevels; ++mip) +{ + // reisze framebuffer according to mip-level size. + unsigned int mipWidth = 128 * std::pow(0.5, mip); + unsigned int mipHeight = 128 * std::pow(0.5, mip); + <function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, captureRBO); + <function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + <function id='22'>glViewport</function>(0, 0, mipWidth, mipHeight); + + float roughness = (float)mip / (float)(maxMipLevels - 1); + prefilterShader.setFloat("roughness", roughness); + for (unsigned int i = 0; i &lt; 6; ++i) + { + prefilterShader.setMat4("view", captureViews[i]); + <function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterMap, mip); + + <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + renderCube(); + } +} +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + The process is similar to the irradiance map convolution, but this time we scale the framebuffer's dimensions to the appropriate mipmap scale, each mip level reducing the dimensions by a scale of 2. Additionally, we specify the mip level we're rendering into in <fun><function id='81'>glFramebufferTexture2D</function></fun>'s last parameter and pass the roughness we're pre-filtering for to the pre-filter shader. +</p> + +<p> + This should give us a properly pre-filtered environment map that returns blurrier reflections the higher mip level we access it from. If we use the pre-filtered environment cubemap in the skybox shader and forcefully sample somewhat above its first mip level like so: +</p> + +<pre><code> +vec3 envColor = textureLod(environmentMap, WorldPos, 1.2).rgb; +</code></pre> + +<p> + We get a result that indeed looks like a blurrier version of the original environment: +</p> + +<img src="/img/pbr/ibl_prefilter_map_sample.png" alt="Visualizing a LOD mip level of the pre-filtered environment map in the skybox."/> + +<p> + If it looks somewhat similar you've successfully pre-filtered the HDR environment map. Play around with different mipmap levels to see the pre-filter map gradually change from sharp to blurry reflections on increasing mip levels. +</p> + + +<h2>Pre-filter convolution artifacts</h2> +<p> + While the current pre-filter map works fine for most purposes, sooner or later you'll come across several render artifacts that are directly related to the pre-filter convolution. I'll list the most common here including how to fix them. +</p> + +<h3>Cubemap seams at high roughness</h3> +<p> + Sampling the pre-filter map on surfaces with a rough surface means sampling the pre-filter map on some of its lower mip levels. When sampling cubemaps, OpenGL by default doesn't linearly interpolate <strong>across</strong> cubemap faces. Because the lower mip levels are both of a lower resolution and the pre-filter map is convoluted with a much larger sample lobe, the lack of <em>between-cube-face filtering</em> becomes quite apparent: +</p> + +<img src="/img/pbr/ibl_prefilter_seams.png" alt="Visible cubemap seams in the pre-filter map."/> + +<p> + Luckily for us, OpenGL gives us the option to properly filter across cubemap faces by enabling <var>GL_TEXTURE_CUBE_MAP_SEAMLESS</var>: +</p> + +<pre><code> +<function id='60'>glEnable</function>(GL_TEXTURE_CUBE_MAP_SEAMLESS); +</code></pre> + +<p> + Simply enable this property somewhere at the start of your application and the seams will be gone. +</p> + +<h3>Bright dots in the pre-filter convolution</h3> +<p> + Due to high frequency details and wildly varying light intensities in specular reflections, convoluting the specular reflections requires a large number of samples to properly account for the wildly varying nature of HDR environmental reflections. We already take a very large number of samples, but on some environments it may still not be enough at some of the rougher mip levels in which case you'll start seeing dotted patterns emerge around bright areas: +</p> + + <img src="/img/pbr/ibl_prefilter_dots.png" alt="Visible dots on high frequency HDR maps in the deeper mip LOD levels of a pre-filter map."/> + +<p> + One option is to further increase the sample count, but this won't be enough for all environments. As described by <a href="https://chetanjags.wordpress.com/2015/08/26/image-based-lighting/" target="_blank">Chetan Jags</a> we can reduce this artifact by (during the pre-filter convolution) not directly sampling the environment map, but sampling a mip level of the environment map based on the integral's PDF and the roughness: +</p> + +<pre><code> +float D = DistributionGGX(NdotH, roughness); +float pdf = (D * NdotH / (4.0 * HdotV)) + 0.0001; + +float resolution = 512.0; // resolution of source cubemap (per face) +float saTexel = 4.0 * PI / (6.0 * resolution * resolution); +float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001); + +float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel); +</code></pre> + +<p> + Don't forget to enable trilinear filtering on the environment map you want to sample its mip levels from: +</p> + +<pre><code> +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, envCubemap); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); +</code></pre> + +<p> + And let OpenGL generate the mipmaps <strong>after</strong> the cubemap's base texture is set: +</p> + +<pre><code> +// convert HDR equirectangular environment map to cubemap equivalent +[...] +// then generate mipmaps +<function id='48'>glBindTexture</function>(GL_TEXTURE_CUBE_MAP, envCubemap); +<function id='51'>glGenerateMipmap</function>(GL_TEXTURE_CUBE_MAP); +</code></pre> + +<p> + This works surprisingly well and should remove most, if not all, dots in your pre-filter map on rougher surfaces. +</p> + +<h2>Pre-computing the BRDF</h2> +<p> + With the pre-filtered environment up and running, we can focus on the second part of the split-sum approximation: the BRDF. Let's briefly review the specular split sum approximation again: +</p> + +\[ + L_o(p,\omega_o) = + \int\limits_{\Omega} L_i(p,\omega_i) d\omega_i + * + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i +\] + +<p> + We've pre-computed the left part of the split sum approximation in the pre-filter map over different roughness levels. The right side requires us to convolute the BRDF equation over the angle \(n \cdot \omega_o\), the surface roughness, and Fresnel's \(F_0\). This is similar to integrating the specular BRDF with a solid-white environment or a constant radiance \(L_i\) of <code>1.0</code>. Convoluting the BRDF over 3 variables is a bit much, but we can try to move \(F_0\) out of the specular BRDF equation: +</p> + +\[ + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i = \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) \frac{F(\omega_o, h)}{F(\omega_o, h)} n \cdot \omega_i d\omega_i +\] + +<p> + With \(F\) being the Fresnel equation. Moving the Fresnel denominator to the BRDF gives us the following equivalent equation: +</p> + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} F(\omega_o, h) n \cdot \omega_i d\omega_i +\] + +<p> + Substituting the right-most \(F\) with the Fresnel-Schlick approximation gives us: + </p> + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + (1 - F_0){(1 - \omega_o \cdot h)}^5) n \cdot \omega_i d\omega_i +\] + +<p> + Let's replace \({(1 - \omega_o \cdot h)}^5\) by \(\alpha\) to make it easier to solve for \(F_0\): +</p> + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + (1 - F_0)\alpha) n \cdot \omega_i d\omega_i +\] + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + 1*\alpha - F_0*\alpha) n \cdot \omega_i d\omega_i +\] + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 * (1 - \alpha) + \alpha) n \cdot \omega_i d\omega_i +\] + +<p> + Then we split the Fresnel function \(F\) over two integrals: +</p> + +\[ + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 * (1 - \alpha)) n \cdot \omega_i d\omega_i + + + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (\alpha) n \cdot \omega_i d\omega_i +\] + +<p> + This way, \(F_0\) is constant over the integral and we can take \(F_0\) out of the integral. Next, we substitute \(\alpha\) back to its original form giving us the final split sum BRDF equation: +</p> + +\[ + F_0 \int\limits_{\Omega} f_r(p, \omega_i, \omega_o)(1 - {(1 - \omega_o \cdot h)}^5) n \cdot \omega_i d\omega_i + + + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) {(1 - \omega_o \cdot h)}^5 n \cdot \omega_i d\omega_i +\] + +<p> + The two resulting integrals represent a scale and a bias to \(F_0\) respectively. Note that as \(f_r(p, \omega_i, \omega_o)\) already contains a term for \(F\) they both cancel out, removing \(F\) from \(f_r\). +</p> + + <p> + In a similar fashion to the earlier convoluted environment maps, we can convolute the BRDF equations on their inputs: the angle between \(n\) and \(\omega_o\), and the roughness. We store the convoluted results in a 2D lookup texture (LUT) known as a <def>BRDF integration</def> map that we later use in our PBR lighting shader to get the final convoluted indirect specular result. +</p> + +<p> + The BRDF convolution shader operates on a 2D plane, using its 2D texture coordinates directly as inputs to the BRDF convolution (<var>NdotV</var> and <var>roughness</var>). The convolution code is largely similar to the pre-filter convolution, except that it now processes the sample vector according to our BRDF's geometry function and Fresnel-Schlick's approximation: +</p> + +<pre><code> +vec2 IntegrateBRDF(float NdotV, float roughness) +{ + vec3 V; + V.x = sqrt(1.0 - NdotV*NdotV); + V.y = 0.0; + V.z = NdotV; + + float A = 0.0; + float B = 0.0; + + vec3 N = vec3(0.0, 0.0, 1.0); + + const uint SAMPLE_COUNT = 1024u; + for(uint i = 0u; i &lt; SAMPLE_COUNT; ++i) + { + vec2 Xi = Hammersley(i, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(Xi, N, roughness); + vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(L.z, 0.0); + float NdotH = max(H.z, 0.0); + float VdotH = max(dot(V, H), 0.0); + + if(NdotL > 0.0) + { + float G = GeometrySmith(N, V, L, roughness); + float G_Vis = (G * VdotH) / (NdotH * NdotV); + float Fc = pow(1.0 - VdotH, 5.0); + + A += (1.0 - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + return vec2(A, B); +} +// ---------------------------------------------------------------------------- +void main() +{ + vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); + FragColor = integratedBRDF; +} +</code></pre> + +<p> + As you can see, the BRDF convolution is a direct translation from the mathematics to code. We take both the angle \(\theta\) and the roughness as input, generate a sample vector with importance sampling, process it over the geometry and the derived Fresnel term of the BRDF, and output both a scale and a bias to \(F_0\) for each sample, averaging them in the end. +</p> + +<p> + You may recall from the <a href="https://learnopengl.com/PBR/Theory" target="_blank">theory</a> chapter that the geometry term of the BRDF is slightly different when used alongside IBL as its \(k\) variable has a slightly different interpretation: +</p> + +\[ + k_{direct} = \frac{(\alpha + 1)^2}{8} +\] + +\[ + k_{IBL} = \frac{\alpha^2}{2} +\] + +<p> + Since the BRDF convolution is part of the specular IBL integral we'll use \(k_{IBL}\) for the Schlick-GGX geometry function: +</p> + +<pre><code> +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float a = roughness; + float k = (a * a) / 2.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} +// ---------------------------------------------------------------------------- +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} +</code></pre> + +<p> + Note that while \(k\) takes <var>a</var> as its parameter we didn't square <var>roughness</var> as <var>a</var> as we originally did for other interpretations of <var>a</var>; likely as <var>a</var> is squared here already. I'm not sure whether this is an inconsistency on Epic Games' part or the original Disney paper, but directly translating <var>roughness</var> to <var>a</var> gives the BRDF integration map that is identical to Epic Games' version. +</p> + +<p> + Finally, to store the BRDF convolution result we'll generate a 2D texture of a 512 by 512 resolution: +</p> + +<pre><code> +unsigned int brdfLUTTexture; +<function id='50'>glGenTextures</function>(1, &brdfLUTTexture); + +// pre-allocate enough memory for the LUT texture. +<function id='48'>glBindTexture</function>(GL_TEXTURE_2D, brdfLUTTexture); +<function id='52'>glTexImage2D</function>(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_FLOAT, 0); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +<function id='15'>glTexParameter</function>i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +</code></pre> + +<p> + Note that we use a 16-bit precision floating format as recommended by Epic Games. Be sure to set the wrapping mode to <var>GL_CLAMP_TO_EDGE</var> to prevent edge sampling artifacts. +</p> + +<p> + Then, we re-use the same framebuffer object and run this shader over an NDC screen-space quad: +</p> + +<pre class="cpp"><code> +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, captureFBO); +<function id='83'>glBindRenderbuffer</function>(GL_RENDERBUFFER, captureRBO); +<function id='88'>glRenderbufferStorage</function>(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); +<function id='81'>glFramebufferTexture2D</function>(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdfLUTTexture, 0); + +<function id='22'>glViewport</function>(0, 0, 512, 512); +brdfShader.use(); +<function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +RenderQuad(); + +<function id='77'>glBindFramebuffer</function>(GL_FRAMEBUFFER, 0); +</code></pre> + +<p> + The convoluted BRDF part of the split sum integral should give you the following result: +</p> + +<img src="/img/pbr/ibl_brdf_lut.png" alt="BRDF LUT"/> + +<p> + With both the pre-filtered environment map and the BRDF 2D LUT we can re-construct the indirect specular integral according to the split sum approximation. The combined result then acts as the indirect or ambient specular light. +</p> + + <h2>Completing the IBL reflectance</h2> +<p> + To get the indirect specular part of the reflectance equation up and running we need to stitch both parts of the split sum approximation together. Let's start by adding the pre-computed lighting data to the top of our PBR shader: +</p> + +<pre><code> +uniform samplerCube prefilterMap; +uniform sampler2D brdfLUT; +</code></pre> + +<p> + First, we get the indirect specular reflections of the surface by sampling the pre-filtered environment map using the reflection vector. Note that we sample the appropriate mip level based on the surface roughness, giving rougher surfaces <em>blurrier</em> specular reflections: +</p> + +<pre><code> +void main() +{ + [...] + vec3 R = reflect(-V, N); + + const float MAX_REFLECTION_LOD = 4.0; + vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; + [...] +} +</code></pre> + +<p> + In the pre-filter step we only convoluted the environment map up to a maximum of 5 mip levels (0 to 4), which we denote here as <var>MAX_REFLECTION_LOD</var> to ensure we don't sample a mip level where there's no (relevant) data. +</p> + +<p> + Then we sample from the BRDF lookup texture given the material's roughness and the angle between the normal and view vector: +</p> + +<pre><code> +vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); +vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; +vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); +</code></pre> + +<p> + Given the scale and bias to \(F_0\) (here we're directly using the indirect Fresnel result <var>F</var>) from the BRDF lookup texture, we combine this with the left pre-filter portion of the IBL reflectance equation and re-construct the approximated integral result as <var>specular</var>. +</p> + +<p> + This gives us the indirect specular part of the reflectance equation. Now, combine this with the diffuse IBL part of the reflectance equation from the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">last</a> chapter and we get the full PBR IBL result: +</p> + +<pre><code> +vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); + +vec3 kS = F; +vec3 kD = 1.0 - kS; +kD *= 1.0 - metallic; + +vec3 irradiance = texture(irradianceMap, N).rgb; +vec3 diffuse = irradiance * albedo; + +const float MAX_REFLECTION_LOD = 4.0; +vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; +vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; +vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); + +vec3 ambient = (kD * diffuse + specular) * ao; +</code></pre> + +<p> + Note that we don't multiply <var>specular</var> by <var>kS</var> as we already have a Fresnel multiplication in there. +</p> + +<p> + Now, running this exact code on the series of spheres that differ by their roughness and metallic properties, we finally get to see their true colors in the final PBR renderer: +</p> + + <img src="/img/pbr/ibl_specular_result.png" alt="Render in OpenGL of full PBR with IBL (image based lighting) on spheres with varying roughness and metallic properties."/> + +<p> + We could even go wild, and use some cool textured <a href="http://freepbr.com" target="_blank">PBR materials</a>: +</p> + + <img src="/img/pbr/ibl_specular_result_textured.png" alt="Render in OpenGL of full PBR with IBL (image based lighting) on textured spheres."/> + +<p> + Or load <a href="http://artisaverb.info/PBT.html" target="_blank">this awesome free 3D PBR model</a> by Andrew Maximov: +</p> + + <img src="/img/pbr/ibl_specular_result_model.png" alt="Render in OpenGL of full PBR with IBL (image based lighting) on a 3D PBR model."/> + +<p> + I'm sure we can all agree that our lighting now looks a lot more convincing. What's even better, is that our lighting looks physically correct regardless of which environment map we use. Below you'll see several different pre-computed HDR maps, completely changing the lighting dynamics, but still looking physically correct without changing a single lighting variable! +</p> + +<img src="/img/pbr/ibl_specular_result_different_environments.png" alt="Render in OpenGL of full PBR with IBL (image based lighting) on a 3D PBR model over multiple different environments (with changing light conditions)."/> + + +<p> + Well, this PBR adventure turned out to be quite a long journey. There are a lot of steps and thus a lot that could go wrong so carefully work your way through the <a href="/code_viewer_gh.php?code=src/6.pbr/2.2.1.ibl_specular/ibl_specular.cpp" target="_blank">sphere scene</a> or <a href="/code_viewer_gh.php?code=src/6.pbr/2.2.2.ibl_specular_textured/ibl_specular_textured.cpp" target="_blank">textured scene</a> code samples (including all shaders) if you're stuck, or check and ask around in the comments. +</p> + +<h3>What's next?</h3> +<p> + Hopefully, by the end of this tutorial you should have a pretty clear understanding of what PBR is about, and even have an actual PBR renderer up and running. In these tutorials, we've pre-computed all the relevant PBR image-based lighting data at the start of our application, before the render loop. This was fine for educational purposes, but not too great for any practical use of PBR. First, the pre-computation only really has to be done once, not at every startup. And second, the moment you use multiple environment maps you'll have to pre-compute each and every one of them at every startup which tends to build up. +</p> + +<p> + For this reason you'd generally pre-compute an environment map into an irradiance and pre-filter map just once, and then store it on disk (note that the BRDF integration map isn't dependent on an environment map so you only need to calculate or load it once). This does mean you'll need to come up with a custom image format to store HDR cubemaps, including their mip levels. Or, you'll store (and load) it as one of the available formats (like .dds that supports storing mip levels). +</p> + +<p> + Furthermore, we've described the <strong>total</strong> process in these tutorials, including generating the pre-computed IBL images to help further our understanding of the PBR pipeline. But, you'll be just as fine by using several great tools like <a href="https://github.com/dariomanesku/cmftStudio" target="_blank">cmftStudio</a> or <a href="https://github.com/derkreature/IBLBaker" target="_blank">IBLBaker</a> to generate these pre-computed maps for you. +</p> + +<p> + One point we've skipped over is pre-computed cubemaps as <def>reflection probes</def>: cubemap interpolation and parallax correction. This is the process of placing several reflection probes in your scene that take a cubemap snapshot of the scene at that specific location, which we can then convolute as IBL data for that part of the scene. By interpolating between several of these probes based on the camera's vicinity we can achieve local high-detail image-based lighting that is simply limited by the amount of reflection probes we're willing to place. This way, the image-based lighting could correctly update when moving from a bright outdoor section of a scene to a darker indoor section for instance. I'll write a tutorial about reflection probes somewhere in the future, but for now I recommend the article by Chetan Jags below to give you a head start. +</p> + + +<h2>Further reading</h2> +<ul> + <li><a href="http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf" target="_blank">Real Shading in Unreal Engine 4</a>: explains Epic Games' split sum approximation. This is the article the IBL PBR code is based of.</li> + <li><a href="http://www.trentreed.net/blog/physically-based-shading-and-image-based-lighting/" target="_blank">Physically Based Shading and Image Based Lighting</a>: great blog post by Trent Reed about integrating specular IBL into a PBR pipeline in real time. </li> + <li><a href="https://chetanjags.wordpress.com/2015/08/26/image-based-lighting/" target="_blank">Image Based Lighting</a>: very extensive write-up by Chetan Jags about specular-based image-based lighting and several of its caveats, including light probe interpolation.</li> + <li><a href="https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf" target="_blank">Moving Frostbite to PBR</a>: well written and in-depth overview of integrating PBR into a AAA game engine by Sébastien Lagarde and Charles de Rousiers.</li> + <li><a href="https://jmonkeyengine.github.io/wiki/jme3/advanced/pbr_part3.html" target="_blank">Physically Based Rendering – Part Three</a>: high level overview of IBL lighting and PBR by the JMonkeyEngine team.</li> + <li><a href="https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/" target="_blank">Implementation Notes: Runtime Environment Map Filtering for Image Based Lighting</a>: extensive write-up by Padraic Hennessy about pre-filtering HDR environment maps and significantly optimizing the sample process.</li> +</ul> + + </div> + + </main> +</body> +</html> diff --git a/pub/PBR/Lighting.html b/pub/PBR/Lighting.html @@ -0,0 +1,744 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Lighting</h1> +<h1 id="content-url" style='display:none;'>PBR/Lighting</h1> +<p> + 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. +</p> + +<p> + Let's start by re-visiting the final reflectance equation from the previous chapter: +</p> + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + 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. +</p> + +<p> + 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\): +</p> + +<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"/> + +<p> + 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>. +</p> + +<p> + 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\). +</p> + +<p> + 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\). + In code this translates to: +</p> + +<pre><code> +vec3 lightColor = vec3(23.47, 21.31, 20.79); +vec3 wi = normalize(lightPos - fragPos); +float cosTheta = max(dot(N, Wi), 0.0); +float attenuation = calculateAttenuation(fragPos, lightPos); +vec3 radiance = lightColor * attenuation * cosTheta; +</code></pre> + +<p> + 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. +</p> + +<note> + 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. +</note> + +<p> + 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. +</p> + +<p> + 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. +</p> + +<h2>A PBR surface model</h2> +<p> + 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: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; +in vec3 WorldPos; +in vec3 Normal; + +uniform vec3 camPos; + +uniform vec3 albedo; +uniform float metallic; +uniform float roughness; +uniform float ao; +</code></pre> + +<p> + 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. +</p> + +<p> + Then at the start of the fragment shader we do the usual calculations required for any lighting algorithm: +</p> + +<pre><code> +void main() +{ + vec3 N = normalize(Normal); + vec3 V = normalize(camPos - WorldPos); + [...] +} +</code></pre> + +<h3>Direct lighting</h3> +<p> + 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: +</p> + +<pre><code> +vec3 Lo = vec3(0.0); +for(int i = 0; i &lt; 4; ++i) +{ + vec3 L = normalize(lightPositions[i] - WorldPos); + vec3 H = normalize(V + L); + + float distance = length(lightPositions[i] - WorldPos); + float attenuation = 1.0 / (distance * distance); + vec3 radiance = lightColors[i] * attenuation; + [...] +</code></pre> + +<p> + 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>. +</p> + +<note> + 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. +</note> + +<p> + Then, for each light we want to calculate the full Cook-Torrance specular BRDF term: +</p> + + \[ + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} + \] + +<p> + 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): +</p> + +<pre><code> +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); +} +</code></pre> + +<p> + 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: +</p> + +<pre><code> +vec3 F0 = vec3(0.04); +F0 = mix(F0, albedo, metallic); +vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); +</code></pre> + +<p> + 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. +</p> + +<p> + Given \(F\), the remaining terms to calculate are the normal distribution function \(D\) and the geometry function \(G\). +</p> + +<p> + In a direct PBR lighting shader their code equivalents are: +</p> + +<pre><code> +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} +</code></pre> + +<p> + 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. +</p> + +<p> + With both functions defined, calculating the NDF and the G term in the reflectance loop is straightforward: +</p> + +<pre><code> +float NDF = DistributionGGX(N, H, roughness); +float G = GeometrySmith(N, V, L, roughness); +</code></pre> + +<p> + This gives us enough to calculate the Cook-Torrance BRDF: +</p> + +<pre><code> +vec3 numerator = NDF * G * F; +float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; +vec3 specular = numerator / denominator; +</code></pre> + +<p> + 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>. +</p> + +<p> + 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\): +</p> + +<pre><code> +vec3 kS = F; +vec3 kD = vec3(1.0) - kS; + +kD *= 1.0 - metallic; +</code></pre> + +<p> + 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: +</p> + +<pre><code> + const float PI = 3.14159265359; + + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; +} +</code></pre> + +<p> + 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. +</p> + +<p> + 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: +</p> + +<pre><code> +vec3 ambient = vec3(0.03) * albedo * ao; +vec3 color = ambient + Lo; +</code></pre> + +<h3>Linear and HDR rendering</h3> +<p> + 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: +</p> + +<pre><code> +color = color / (color + vec3(1.0)); +color = pow(color, vec3(1.0/2.2)); +</code></pre> + +<p> + 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. +</p> + + <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."/> + +<p> + 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. +</p> + +<h3>Full direct lighting PBR shader</h3> +<p> + 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: +</p> + +<pre><code> +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; +in vec3 WorldPos; +in vec3 Normal; + +// material parameters +uniform vec3 albedo; +uniform float metallic; +uniform float roughness; +uniform float ao; + +// lights +uniform vec3 lightPositions[4]; +uniform vec3 lightColors[4]; + +uniform vec3 camPos; + +const float PI = 3.14159265359; + +float DistributionGGX(vec3 N, vec3 H, float roughness); +float GeometrySchlickGGX(float NdotV, float roughness); +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness); +vec3 fresnelSchlick(float cosTheta, vec3 F0); + +void main() +{ + vec3 N = normalize(Normal); + vec3 V = normalize(camPos - WorldPos); + + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metallic); + + // reflectance equation + vec3 Lo = vec3(0.0); + for(int i = 0; i &lt; 4; ++i) + { + // calculate per-light radiance + vec3 L = normalize(lightPositions[i] - WorldPos); + vec3 H = normalize(V + L); + float distance = length(lightPositions[i] - WorldPos); + float attenuation = 1.0 / (distance * distance); + vec3 radiance = lightColors[i] * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; + vec3 specular = numerator / denominator; + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + } + + vec3 ambient = vec3(0.03) * albedo * ao; + vec3 color = ambient + Lo; + + color = color / (color + vec3(1.0)); + color = pow(color, vec3(1.0/2.2)); + + FragColor = vec4(color, 1.0); +} +</code></pre> + +<p> + 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: +</p> + + <img src="/img/pbr/lighting_result.png" alt="Render of PBR spheres with varying roughness and metallic values in OpenGL."/> + +<p> + 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. +</p> + +<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> + +<p> + 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>. +</p> + +<h2>Textured PBR</h2> +<p> + 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: +</p> + +<pre><code> +[...] +uniform sampler2D albedoMap; +uniform sampler2D normalMap; +uniform sampler2D metallicMap; +uniform sampler2D roughnessMap; +uniform sampler2D aoMap; + +void main() +{ + vec3 albedo = pow(texture(albedoMap, TexCoords).rgb, 2.2); + vec3 normal = getNormalFromNormalMap(); + float metallic = texture(metallicMap, TexCoords).r; + float roughness = texture(roughnessMap, TexCoords).r; + float ao = texture(aoMap, TexCoords).r; + [...] +} +</code></pre> + +<p> + 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. +</p> + +<p> + 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: +</p> + + <img src="/img/pbr/lighting_textured.png" alt="Render of PBR spheres with a textured PBR material in OpenGL."/> + +<p> + 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. +</p> + +<p> + 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. +</p> + + </div> + + </main> +</body> +</html> diff --git a/pub/PBR/Theory.html b/pub/PBR/Theory.html @@ -0,0 +1,925 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Theory</h1> +<h1 id="content-url" style='display:none;'>PBR/Theory</h1> +<p> + PBR, or more commonly known as <def>physically based rendering</def>, is a collection of render techniques that are more or less based on the same underlying theory that more closely matches that of the physical world. As physically based rendering aims to mimic light in a physically plausible way, it generally looks more realistic compared to our original lighting algorithms like Phong and Blinn-Phong. Not only does it look better, as it closely approximates actual physics, we (and especially the artists) can author surface materials based on physical parameters without having to resort to cheap hacks and tweaks to make the lighting look right. One of the bigger advantages of authoring materials based on physical parameters is that these materials will look correct regardless of lighting conditions; something that is not true in non-PBR pipelines. +</p> + +<p> + Physically based rendering is still nonetheless an approximation of reality (based on the principles of physics) which is why it's not called physical shading, but physically <em>based</em> shading. For a PBR lighting model to be considered physically based, it has to satisfy the following 3 conditions (don't worry, we'll get to them soon enough): +</p> + +<ol> + <li>Be based on the microfacet surface model.</li> + <li>Be energy conserving.</li> + <li>Use a physically based BRDF.</li> +</ol> + +<p> + In the next PBR chapters we'll be focusing on the PBR approach as originally explored by Disney and adopted for real-time display by Epic Games. Their approach, based on the <def>metallic workflow</def>, is decently documented, widely adopted on most popular engines, and looks visually amazing. By the end of these chapters we'll have something that looks like this: +</p> + +<img src="/img/pbr/ibl_specular_result_textured.png" class="" alt="An example of a PBR render (with IBL) in OpenGL on textured materials."/> + +<p> + Keep in mind, the topics in these chapters are rather advanced so it is advised to have a good understanding of OpenGL and shader lighting. Some of the more advanced knowledge you'll need for this series are: <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers" target="_blank">framebuffers</a>, <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps" target="_blank">cubemaps</a>, <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction" target="_blank">gamma correction</a>, <a href="https://learnopengl.com/Advanced-Lighting/HDR" target="_blank">HDR</a>, and <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" target="_blank">normal mapping</a>. We'll also delve into some advanced mathematics, but I'll do my best to explain the concepts as clear as possible. +</p> + +<h2>The microfacet model</h2> +<p> + All the PBR techniques are based on the theory of microfacets. The theory describes that any surface at a microscopic scale can be described by tiny little perfectly reflective mirrors called <def>microfacets</def>. Depending on the roughness of a surface, the alignment of these tiny little mirrors can differ quite a lot: +</p> + +<img src="/img/pbr/microfacets.png" class="clean" alt="Different surface types for OpenGL PBR"/> + +<p> + The rougher a surface is, the more chaotically aligned each microfacet will be along the surface. The effect of these tiny-like mirror alignments is, that when specifically talking about specular lighting/reflection, the incoming light rays are more likely to <def>scatter</def> along completely different directions on rougher surfaces, resulting in a more widespread specular reflection. In contrast, on a smooth surface the light rays are more likely to reflect in roughly the same direction, giving us smaller and sharper reflections: +</p> + +<img src="/img/pbr/microfacets_light_rays.png" class="clean" alt="Effect of light scattering on different surface types for OpenGL PBR"/> + +<p> + No surface is completely smooth on a microscopic level, but seeing as these microfacets are small enough that we can't make a distinction between them on a per-pixel basis, we statistically approximate the surface's microfacet roughness given a <def>roughness</def> parameter. Based on the roughness of a surface, we can calculate the ratio of microfacets roughly aligned to some vector \(h\). This vector \(h\) is the <def>halfway vector</def> that sits halfway between the light \(l\) and view \(v\) vector. We've discussed the halfway vector before in the <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting" target="_blank">advanced lighting</a> chapter which is calculated as the sum of \(l\) and \(v\) divided by its length: +</p> + +\[ + h = \frac{l + v}{\|l + v\|} +\] + +<p> + The more the microfacets are aligned to the halfway vector, the sharper and stronger the specular reflection. Together with a roughness parameter that varies between 0 and 1, we can statistically approximate the alignment of the microfacets: +</p> + +<img src="/img/pbr/ndf.png" alt="Visualized NDF (Normalized Distribution Function) in OpenGL PBR"/> + +<p> + We can see that higher roughness values display a much larger specular reflection shape, in contrast with the smaller and sharper specular reflection shape of smooth surfaces. +</p> + +<h2>Energy conservation</h2> +<p> + The microfacet approximation employs a form of <def>energy conservation</def>: outgoing light energy should never exceed the incoming light energy (excluding emissive surfaces). Looking at the above image we see the specular reflection area increase, but also its brightness decrease at increasing roughness levels. If the specular intensity were to be the same at each pixel (regardless of the size of the specular shape) the rougher surfaces would emit much more energy, violating the energy conservation principle. This is why we see specular reflections more intensely on smooth surfaces and more dimly on rough surfaces. +</p> + +<p> + For energy conservation to hold, we need to make a clear distinction between diffuse and specular light. The moment a light ray hits a surface, it gets split in both a <def>refraction</def> part and a <def>reflection</def> part. The reflection part is light that directly gets reflected and doesn't enter the surface; this is what we know as specular lighting. The refraction part is the remaining light that enters the surface and gets absorbed; this is what we know as diffuse lighting. + </p> + +<p> + There are some nuances here as refracted light doesn't immediately get absorbed by touching the surface. From physics, we know that light can be modeled as a beam of energy that keeps moving forward until it loses all of its energy; the way a light beam loses energy is by collision. Each material consists of tiny little particles that can collide with the light ray as illustrated in the following image. The particles absorb some, or all, of the light's energy at each collision which is converted into heat. +</p> + +<img src="/img/pbr/surface_reaction.png" class="clean" alt="Light as reflected and refracted light with absorption in OpenGL PBR"/> + +<p> + Generally, not all energy is absorbed and the light will continue to <def>scatter</def> in a (mostly) random direction at which point it collides with other particles until its energy is depleted or it leaves the surface again. Light rays re-emerging out of the surface contribute to the surface's observed (diffuse) color. In physically based rendering however, we make the simplifying assumption that all refracted light gets absorbed and scattered at a very small area of impact, ignoring the effect of scattered light rays that would've exited the surface at a distance. Specific shader techniques that do take this into account are known as <def>subsurface scattering</def> techniques that significantly improve the visual quality on materials like skin, marble, or wax, but come at the price of performance. + </p> + +<p> + An additional subtlety when it comes to reflection and refraction are surfaces that are <def>metallic</def>. Metallic surfaces react different to light compared to non-metallic surfaces (also known as <def>dielectrics</def>). Metallic surfaces follow the same principles of reflection and refraction, but <strong>all</strong> refracted light gets directly absorbed without scattering. This means metallic surfaces only leave reflected or specular light; metallic surfaces show no diffuse colors. Because of this apparent distinction between metals and dielectrics, they're both treated differently in the PBR pipeline which we'll delve into further down the chapter. +</p> + +<p> + This distinction between reflected and refracted light brings us to another observation regarding energy preservation: they're <strong>mutually exclusive</strong>. Whatever light energy gets reflected will no longer be absorbed by the material itself. Thus, the energy left to enter the surface as refracted light is directly the resulting energy after we've taken reflection into account. +</p> + +<p> + We preserve this energy conserving relation by first calculating the specular fraction that amounts the percentage the incoming light's energy is reflected. The fraction of refracted light is then directly calculated from the specular fraction as: +</p> + +<pre><code> +float kS = calculateSpecularComponent(...); // reflection/specular fraction +float kD = 1.0 - kS; // refraction/diffuse fraction +</code></pre> + +<p> + This way we know both the amount the incoming light reflects and the amount the incoming light refracts, while adhering to the energy conservation principle. Given this approach, it is impossible for both the refracted/diffuse and reflected/specular contribution to exceed <code>1.0</code>, thus ensuring the sum of their energy never exceeds the incoming light energy. Something we did not take into account in the previous lighting chapters. +</p> + +<h2>The reflectance equation</h2> +<p> + This brings us to something called the <a href="https://en.wikipedia.org/wiki/Rendering_equation" target="_blank">render equation</a>, an elaborate equation some very smart folks out there came up with that is currently the best model we have for simulating the visuals of light. Physically based rendering strongly follows a more specialized version of the render equation known as the <def>reflectance equation</def>. To properly understand PBR, it's important to first build a solid understanding of the reflectance equation: +</p> + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + The reflectance equation appears daunting at first, but as we'll dissect it you'll see it slowly starts to makes sense. To understand the equation, we have to delve into a bit of <def>radiometry</def>. Radiometry is the measurement of electromagnetic radiation, including visible light. There are several radiometric quantities we can use to measure light over surfaces and directions, but we will only discuss a single one that's relevant to the reflectance equation known as <def>radiance</def>, denoted here as \(L\). Radiance is used to quantify the magnitude or strength of light coming from a single direction. It's a bit tricky to understand at first as radiance is a combination of multiple physical quantities so we'll focus on those first: +</p> + +<p> + <strong>Radiant flux</strong>: radiant flux \(\Phi\) is the transmitted energy of a light source measured in Watts. Light is a collective sum of energy over multiple different wavelengths, each wavelength associated with a particular (visible) color. The emitted energy of a light source can therefore be thought of as a function of all its different wavelengths. Wavelengths between 390nm to 700nm (nanometers) are considered part of the visible light spectrum i.e. wavelengths the human eye is able to perceive. Below you'll find an image of the different energies per wavelength of daylight: +</p> + + <img src="/img/pbr/daylight_spectral_distribution.png" class="clean" alt="Spectral distribution of daylight"/> + +<p> + The radiant flux measures the total area of this function of different wavelengths. Directly taking this measure of wavelengths as input is slightly impractical so we often make the simplification of representing radiant flux, not as a function of varying wavelength strengths, but as a light color triplet encoded as <code>RGB</code> (or as we'd commonly call it: light color). This encoding does come at quite a loss of information, but this is generally negligible for visual aspects. +</p> + +<p> + <strong>Solid angle</strong>: the solid angle, denoted as \(\omega\), tells us the size or area of a shape projected onto a unit sphere. The area of the projected shape onto this unit sphere is known as the <def>solid angle</def>; you can visualize the solid angle as a direction with volume: +</p> + + <img src="/img/pbr/solid_angle.png" class="clean" alt="Solid angle"/> + + <p> + Think of being an observer at the center of this unit sphere and looking in the direction of the shape; the size of the silhouette you make out of it is the solid angle. +</p> + +<p> + <strong>Radiant intensity</strong>: radiant intensity measures the amount of radiant flux per solid angle, or the strength of a light source over a projected area onto the unit sphere. For instance, given an omnidirectional light that radiates equally in all directions, the radiant intensity can give us its energy over a specific area (solid angle): +</p> + + <img src="/img/pbr/radiant_intensity.png" class="clean" alt="Radiant intensity"/> + +<p> + The equation to describe the radiant intensity is defined as follows: +</p> + + \[I = \frac{d\Phi}{d\omega}\] + +<p> + Where \(I\) is the radiant flux \(\Phi\) over the solid angle \(\omega\). +</p> + +<p> + With knowledge of radiant flux, radiant intensity, and the solid angle, we can finally describe the equation for <strong>radiance</strong>. Radiance is described as the total observed energy in an area \(A\) over the solid angle \(\omega\) of a light of radiant intensity \(\Phi\): +</p> + + \[L=\frac{d^2\Phi}{ dA d\omega \cos\theta}\] + + <img src="/img/pbr/radiance.png" class="clean" alt="Diagram of radiance"/> + +<p> + Radiance is a radiometric measure of the amount of light in an area, scaled by the <def>incident</def> (or incoming) angle \(\theta\) of the light to the surface's normal as \(\cos \theta\): light is weaker the less it directly radiates onto the surface, and strongest when it is directly perpendicular to the surface. This is similar to our perception of diffuse lighting from the <a href="https://learnopengl.com/Lighting/Basic-lighting" target="_blank">basic lighting</a> chapter as \(\cos\theta\) directly corresponds to the dot product between the light's direction vector and the surface normal: +</p> + +<pre><code> +float cosTheta = dot(lightDir, N); +</code></pre> + +<p> + The radiance equation is quite useful as it contains most physical quantities we're interested in. If we consider the solid angle \(\omega\) and the area \(A\) to be infinitely small, we can use radiance to measure the flux of a single ray of light hitting a single point in space. This relation allows us to calculate the radiance of a single light ray influencing a single (fragment) point; we effectively translate the solid angle \(\omega\) into a direction vector \(\omega\), and \(A\) into a point \(p\). This way, we can directly use radiance in our shaders to calculate a single light ray's per-fragment contribution. +</p> + +<p> + In fact, when it comes to radiance we generally care about <strong>all</strong> incoming light onto a point \(p\), which is the sum of all radiance known as <def>irradiance</def>. With knowledge of both radiance and irradiance we can get back to the reflectance equation: +</p> + + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + We now know that \(L\) in the render equation represents the radiance of some point \(p\) and some incoming infinitely small solid angle \(\omega_i\) which can be thought of as an incoming direction vector \(\omega_i\). Remember that \(\cos \theta\) scales the energy based on the light's incident angle to the surface, which we find in the reflectance equation as \(n \cdot \omega_i\). The reflectance equation calculates the sum of reflected radiance \(L_o(p, \omega_o)\) of a point \(p\) in direction \(\omega_o\) which is the outgoing direction to the viewer. Or to put it differently: \(L_o\) measures the reflected sum of the lights' irradiance onto point \(p\) as viewed from \(\omega_o\). +</p> + +<p> + The reflectance equation is based around irradiance, which is the sum of all incoming radiance we measure light of. Not just of a single incoming light direction, but of all incoming light directions within a hemisphere \(\Omega\) centered around point \(p\). A <def>hemisphere</def> can be described as half a sphere aligned around a surface's normal \(n\): + </p> + + <img src="/img/pbr/hemisphere.png" class="clean" alt="Hemisphere"/> + +<p> + To calculate the total of values inside an area or (in the case of a hemisphere) a volume, we use a mathematical construct called an <def>integral</def> denoted in the reflectance equation as \(\int\) over all incoming directions \(d\omega_i\) within the hemisphere \(\Omega\) . An integral measures the area of a function, which can either be calculated analytically or numerically. As there is no analytical solution to both the render and reflectance equation, we'll want to numerically solve the integral discretely. This translates to taking the result of small discrete steps of the reflectance equation over the hemisphere \(\Omega\) and averaging their results over the step size. This is known as the <def>Riemann sum</def> that we can roughly visualize in code as follows: +</p> + +<pre><code> +int steps = 100; +float sum = 0.0f; +vec3 P = ...; +vec3 Wo = ...; +vec3 N = ...; +float dW = 1.0f / steps; +for(int i = 0; i &lt; steps; ++i) +{ + vec3 Wi = getNextIncomingLightDir(i); + sum += Fr(P, Wi, Wo) * L(P, Wi) * dot(N, Wi) * dW; +} +</code></pre> + +<p> + By scaling the steps by <code>dW</code>, the sum will equal the total area or volume of the integral function. The <code>dW</code> to scale each discrete step can be thought of as \(d\omega_i\) in the reflectance equation. Mathematically \(d\omega_i\) is the continuous symbol over which we calculate the integral, and while it does not directly relate to <code>dW</code> in code (as this is a discrete step of the Riemann sum), it helps to think of it this way. Keep in mind that taking discrete steps will always give us an approximation of the total area of the function. A careful reader will notice we can increase the <em>accuracy</em> of the Riemann Sum by increasing the number of steps. + </p> + +<p> + The reflectance equation sums up the radiance of all incoming light directions \(\omega_i\) over the hemisphere \(\Omega\) scaled by \(f_r\) that hit point \(p\) and returns the sum of reflected light \(L_o\) in the viewer's direction. The incoming radiance can come from <a href="https://learnopengl.com/PBR/Lighting" target="_blank">light sources</a> as we're familiar with, or from an environment map measuring the radiance of every incoming direction as we'll discuss in the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">IBL</a> chapters. +</p> + +<p> + Now the only unknown left is the \(f_r\) symbol known as the <def>BRDF</def> or <def>bidirectional reflective distribution function</def> that scales or weighs the incoming radiance based on the surface's material properties. +</p> + + +<h2>BRDF</h2> +<p> + The <def>BRDF</def>, or <def>bidirectional reflective distribution function</def>, is a function that takes as input the incoming (light) direction \(\omega_i\), the outgoing (view) direction \(\omega_o\), the surface normal \(n\), and a surface parameter \(a\) that represents the microsurface's roughness. The BRDF approximates how much each individual light ray \(\omega_i\) contributes to the final reflected light of an opaque surface given its material properties. For instance, if the surface has a perfectly smooth surface (~like a mirror) the BRDF function would return 0.0 for all incoming light rays \(\omega_i\) except the one ray that has the same (reflected) angle as the outgoing ray \(\omega_o\) at which the function returns 1.0. </p> + + <p> + A BRDF approximates the material's reflective and refractive properties based on the previously discussed microfacet theory. For a BRDF to be physically plausible it has to respect the law of energy conservation i.e. the sum of reflected light should never exceed the amount of incoming light. Technically, Blinn-Phong is considered a BRDF taking the same \(\omega_i\) and \(\omega_o\) as inputs. However, Blinn-Phong is not considered physically based as it doesn't adhere to the energy conservation principle. There are several physically based BRDFs out there to approximate the surface's reaction to light. However, almost all real-time PBR render pipelines use a BRDF known as the <def>Cook-Torrance BRDF</def>. + </p> + +<p> + The Cook-Torrance BRDF contains both a diffuse and specular part: +</p> + + \[f_r = k_d f_{lambert} + k_s f_{cook-torrance}\] + +<p> + Here \(k_d\) is the earlier mentioned ratio of incoming light energy that gets <em>refracted</em> with \(k_s\) being the ratio that gets <em>reflected</em>. The left side of the BRDF states the diffuse part of the equation denoted here as \(f_{lambert}\). This is known as <def>Lambertian diffuse</def> similar to what we used for diffuse shading, which is a constant factor denoted as: +</p> + + \[ f_{lambert} = \frac{c}{\pi}\] + +<p> + With \(c\) being the albedo or surface color (think of the diffuse surface texture). The divide by pi is there to normalize the diffuse light as the earlier denoted integral that contains the BRDF is scaled by \(\pi\) (we'll get to that in the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">IBL</a> chapters). +</p> + + <note> + You may wonder how this Lambertian diffuse relates to the diffuse lighting we've been using before: the surface color multiplied by the dot product between the surface's normal and the light direction. The dot product is still there, but moved out of the BRDF as we find \(n \cdot \omega_i\) at the end of the \(L_o\) integral. + </note> + +<p> + There exist different equations for the diffuse part of the BRDF which tend to look more realistic, but are also more computationally expensive. As concluded by Epic Games however, the Lambertian diffuse is sufficient enough for most real-time rendering purposes. +</p> + +<p> + The specular part of the BRDF is a bit more advanced and is described as: +</p> + + \[ + f_{CookTorrance} = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} + \] + +<p> + The Cook-Torrance specular BRDF is composed three functions and a normalization factor in the denominator. Each of the D, F and G symbols represent a type of function that approximates a specific part of the surface's reflective properties. These are defined as the normal <strong>D</strong>istribution function, the <strong>F</strong>resnel equation and the <strong>G</strong>eometry function: +</p> + +<ul> + <li><strong>Normal distribution function</strong>: approximates the amount the surface's microfacets are aligned to the halfway vector, influenced by the roughness of the surface; this is the primary function approximating the microfacets.</li> + <li><strong>Geometry function</strong>: describes the self-shadowing property of the microfacets. When a surface is relatively rough, the surface's microfacets can overshadow other microfacets reducing the light the surface reflects.</li> + <li><strong>Fresnel equation</strong>: The Fresnel equation describes the ratio of surface reflection at different surface angles.</li> +</ul> + +<p> + Each of these functions are an approximation of their physics equivalents and you'll find more than one version of each that aims to approximate the underlying physics in different ways; some more realistic, others more efficient. It is perfectly fine to pick whatever approximated version of these functions you want to use. Brian Karis from Epic Games did a great deal of research on the multiple types of approximations <a href="http://graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html" target="_blank">here</a>. We're going to pick the same functions used by Epic Game's Unreal Engine 4 which are the Trowbridge-Reitz GGX for D, the Fresnel-Schlick approximation for F, and the Smith's Schlick-GGX for G. +</p> + +<h3>Normal distribution function</h3> +<p> + The <def>normal distribution function</def> \(D\) statistically approximates the relative surface area of microfacets exactly aligned to the (halfway) vector \(h\). There are a multitude of NDFs that statistically approximate the general alignment of the microfacets given some roughness parameter and the one we'll be using is known as the Trowbridge-Reitz GGX: +</p> + + \[ + NDF_{GGX TR}(n, h, \alpha) = \frac{\alpha^2}{\pi((n \cdot h)^2 (\alpha^2 - 1) + 1)^2} + \] + +<p> + Here \(h\) is the halfway vector to measure against the surface's microfacets, with \(a\) being a measure of the surface's roughness. If we take \(h\) as the halfway vector between the surface normal and light direction over varying roughness parameters we get the following visual result: +</p> + + <img src="/img/pbr/ndf.png" alt="Visualized NDF in OpenGL PBR"/> + +<p> + When the roughness is low (thus the surface is smooth), a highly concentrated number of microfacets are aligned to halfway vectors over a small radius. Due to this high concentration, the NDF displays a very bright spot. On a rough surface however, where the microfacets are aligned in much more random directions, you'll find a much larger number of halfway vectors \(h\) somewhat aligned to the microfacets (but less concentrated), giving us the more grayish results. +</p> + +<p> + In GLSL the Trowbridge-Reitz GGX normal distribution function translates to the following code: +</p> + +<pre><code> +float DistributionGGX(vec3 N, vec3 H, float a) +{ + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / denom; +} +</code></pre> + + +<h3>Geometry function</h3> +<p> + The geometry function statistically approximates the relative surface area where its micro surface-details overshadow each other, causing light rays to be occluded. +</p> + + <img src="/img/pbr/geometry_shadowing.png" class="clean" alt="Light being either shadowed or obstructed due to microfacet model."/> + +<p> + Similar to the NDF, the Geometry function takes a material's roughness parameter as input with rougher surfaces having a higher probability of overshadowing microfacets. The geometry function we will use is a combination of the GGX and Schlick-Beckmann approximation known as Schlick-GGX: +</p> + + \[ + G_{SchlickGGX}(n, v, k) + = + \frac{n \cdot v} + {(n \cdot v)(1 - k) + k } + \] + +<p> + Here \(k\) is a remapping of \(\alpha\) based on whether we're using the geometry function for either direct lighting or IBL lighting: +</p> + +\[ + k_{direct} = \frac{(\alpha + 1)^2}{8} +\] + +\[ + k_{IBL} = \frac{\alpha^2}{2} +\] + +<p> + Note that the value of \(\alpha\) may differ based on how your engine translates roughness to \(\alpha\). In the following chapters we'll extensively discuss how and where this remapping becomes relevant. + </p> + +<p> + To effectively approximate the geometry we need to take account of both the view direction (geometry obstruction) and the light direction vector (geometry shadowing). We can take both into account using <def>Smith's method</def>: +</p> + +\[ + G(n, v, l, k) = G_{sub}(n, v, k) G_{sub}(n, l, k) +\] + +<p> + Using Smith's method with Schlick-GGX as \(G_{sub}\) gives the following visual appearance over varying roughness <code>R</code>: +</p> + + + <img src="/img/pbr/geometry.png" alt="Visualized Geometry function in OpenGL PBR"/> + +<p> + The geometry function is a multiplier between [0.0, 1.0] with 1.0 (or white) measuring no microfacet shadowing, and 0.0 (or black) complete microfacet shadowing. +</p> + +<p> + In GLSL the geometry function translates to the following code: +</p> + +<pre><code> +float GeometrySchlickGGX(float NdotV, float k) +{ + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float k) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx1 = GeometrySchlickGGX(NdotV, k); + float ggx2 = GeometrySchlickGGX(NdotL, k); + + return ggx1 * ggx2; +} +</code></pre> + + +<h3>Fresnel equation</h3> +<p> + The Fresnel equation (pronounced as Freh-nel) describes the ratio of light that gets reflected over the light that gets refracted, which varies over the angle we're looking at a surface. The moment light hits a surface, based on the surface-to-view angle, the Fresnel equation tells us the percentage of light that gets reflected. From this ratio of reflection and the energy conservation principle we can directly obtain the refracted portion of light. + </p> + + <p> + Every surface or material has a level of <def>base reflectivity</def> when looking straight at its surface, but when looking at the surface from an angle <a href="http://filmicworlds.com/blog/everything-has-fresnel/" target="_blank">all</a> reflections become more apparent compared to the surface's base reflectivity. You can check this for yourself by looking at your (presumably) wooden/metallic desk which has a certain level of base reflectivity from a perpendicular view angle, but by looking at your desk from an almost 90 degree angle you'll see the reflections become much more apparent. All surfaces theoretically fully reflect light if seen from perfect 90-degree angles. This phenomenon is known as <def>Fresnel</def> and is described by the Fresnel equation. +</p> + +<p> + The Fresnel equation is a rather complex equation, but luckily the Fresnel equation can be approximated using the <def>Fresnel-Schlick</def> approximation: +</p> + +\[ + F_{Schlick}(h, v, F_0) = + F_0 + (1 - F_0) ( 1 - (h \cdot v))^5 +\] + +<p> + \(F_0\) represents the base reflectivity of the surface, which we calculate using something called the <em>indices of refraction</em> or IOR. As you can see on a sphere surface, the more we look towards the surface's grazing angles (with the halfway-view angle reaching 90 degrees), the stronger the Fresnel and thus the reflections: +</p> + + <img src="/img/pbr/fresnel.png" alt="Visualized Fresnel equation on a sphere."/> + +<p> + There are a few subtleties involved with the Fresnel equation. One is that the Fresnel-Schlick approximation is only really defined for <def>dielectric</def> or non-metal surfaces. For <def>conductor</def> surfaces (metals), calculating the base reflectivity with indices of refraction doesn't properly hold and we need to use a different Fresnel equation for conductors altogether. As this is inconvenient, we further approximate by pre-computing the surface's response at <def>normal incidence</def> (\(F_0\)) at a 0 degree angle as if looking directly onto a surface. We interpolate this value based on the view angle, as per the Fresnel-Schlick approximation, such that we can use the same equation for both metals and non-metals. +</p> + +<p> + The surface's response at normal incidence, or the base reflectivity, can be found in large databases like <a href="http://refractiveindex.info/" target="_blank">these</a> with some of the more common values listed below as taken from Naty Hoffman's course notes: +</p> + +<table> + <tr> + <th>Material</th> + <th>\(F_0\) (Linear)</th> + <th>\(F_0\) (sRGB)</th> + <th>Color</th> + </tr> + <tr> + <td>Water</td> + <td><code>(0.02, 0.02, 0.02)</code></td> + <td><code>&nbsp;(0.15, 0.15, 0.15)</code>&nbsp;&nbsp;</td> + <td style="background-color: #262626"></td> + </tr> + <tr> + <td>Plastic / Glass (Low)</td> + <td><code>(0.03, 0.03, 0.03)</code></td> + <td><code>(0.21, 0.21, 0.21)</code></td> + <td style="background-color: #363636"></td> + </tr> + <tr> + <td>Plastic High</td> + <td><code>(0.05, 0.05, 0.05)</code></td> + <td><code>(0.24, 0.24, 0.24)</code></td> + <td style="background-color: #3D3D3D"></td> + </tr> + <tr> + <td>Glass (high) / Ruby</td> + <td><code>(0.08, 0.08, 0.08)</code></td> + <td><code>(0.31, 0.31, 0.31)</code></td> + <td style="background-color: #4F4F4F"></td> + </tr> + <tr> + <td>Diamond</td> + <td><code>(0.17, 0.17, 0.17)</code></td> + <td><code>(0.45, 0.45, 0.45)</code></td> + <td style="background-color: #737373"></td> + </tr> + <tr> + <td>Iron</td> + <td><code>(0.56, 0.57, 0.58)</code></td> + <td><code>(0.77, 0.78, 0.78)</code></td> + <td style="background-color: #C5C8C8"></td> + </tr> + <tr> + <td>Copper</td> + <td><code>(0.95, 0.64, 0.54)</code></td> + <td><code>(0.98, 0.82, 0.76)</code></td> + <td style="background-color: #FBD2C3"></td> + </tr> + <tr> + <td>Gold</td> + <td><code>(1.00, 0.71, 0.29)</code></td> + <td><code>(1.00, 0.86, 0.57)</code></td> + <td style="background-color: #FFDC92"></td> + </tr> + <tr> + <td>Aluminium</td> + <td><code>(0.91, 0.92, 0.92)</code></td> + <td><code>(0.96, 0.96, 0.97)</code></td> + <td style="background-color: #F6F6F8"></td> + </tr> + <tr> + <td>Silver</td> + <td><code>(0.95, 0.93, 0.88)</code></td> + <td><code>(0.98, 0.97, 0.95)</code></td> + <td style="background-color: #FBF8F3"></td> + </tr> + +</table> + +<p> + What is interesting to observe here is that for all dielectric surfaces the base reflectivity never gets above 0.17 which is the exception rather than the rule, while for conductors the base reflectivity starts much higher and (mostly) varies between 0.5 and 1.0. Furthermore, for conductors (or metallic surfaces) the base reflectivity is tinted. This is why \(F_0\) is presented as an RGB triplet (reflectivity at normal incidence can vary per wavelength); this is something we <strong>only</strong> see at metallic surfaces. +</p> + +<p> + These specific attributes of metallic surfaces compared to dielectric surfaces gave rise to something called the <def>metallic workflow</def>. In the metallic workflow we author surface materials with an extra parameter known as <def>metalness</def> that describes whether a surface is either a metallic or a non-metallic surface. +</p> + +<note> + Theoretically, the metalness of a material is binary: it's either a metal or it isn't; it can't be both. However, most render pipelines allow configuring the metalness of a surface linearly between 0.0 and 1.0. This is mostly because of the lack of material texture precision. For instance, a surface having small (non-metal) dust/sand-like particles/scratches over a metallic surface is difficult to render with binary metalness values. +</note> + +<p> + By pre-computing \(F_0\) for both dielectrics and conductors we can use the same Fresnel-Schlick approximation for both types of surfaces, but we do have to tint the base reflectivity if we have a metallic surface. We generally accomplish this as follows: +</p> + +<pre><code> +vec3 F0 = vec3(0.04); +F0 = mix(F0, surfaceColor.rgb, metalness); +</code></pre> + +<p> + We define a base reflectivity that is approximated for most dielectric surfaces. This is yet another approximation as \(F_0\) is averaged around most common dielectrics. A base reflectivity of 0.04 holds for most dielectrics and produces physically plausible results without having to author an additional surface parameter. Then, based on how metallic a surface is, we either take the dielectric base reflectivity or take \(F_0\) authored as the surface color. Because metallic surfaces absorb all refracted light they have no diffuse reflections and we can directly use the surface color texture as their base reflectivity. +</p> + + <p> + In code, the Fresnel Schlick approximation translates to: +</p> + +<pre><code> +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} +</code></pre> + +<p> + With <code>cosTheta</code> being the dot product result between the surface's normal \(n\) and the halfway \(h\) (or view \(v\)) direction. +</p> + + +<h3>Cook-Torrance reflectance equation</h3> +<p> + With every component of the Cook-Torrance BRDF described, we can include the physically based BRDF into the now final reflectance equation: +</p> + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + This equation is not fully mathematically correct however. You may remember that the Fresnel term \(F\) represents the ratio of light that gets <em>reflected</em> on a surface. This is effectively our ratio \(k_s\), meaning the specular (BRDF) part of the reflectance equation implicitly contains the reflectance ratio \(k_s\). Given this, our final final reflectance equation becomes: +</p> + + \[ + L_o(p,\omega_o) = \int\limits_{\Omega} + (k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) + L_i(p,\omega_i) n \cdot \omega_i d\omega_i + \] + +<p> + This equation now completely describes a physically based render model that is generally recognized as what we commonly understand as physically based rendering, or PBR. Don't worry if you didn't yet completely understand how we'll need to fit all the discussed mathematics together in code. In the next chapters, we'll explore how to utilize the reflectance equation to get much more physically plausible results in our rendered lighting and all the bits and pieces should slowly start to fit together. +</p> + +<h2>Authoring PBR materials</h2> +<p> + With knowledge of the underlying mathematical model of PBR we'll finalize the discussion by describing how artists generally author the physical properties of a surface that we can directly feed into the PBR equations. Each of the surface parameters we need for a PBR pipeline can be defined or modeled by textures. Using textures gives us per-fragment control over how each specific surface point should react to light: whether that point is metallic, rough or smooth, or how the surface responds to different wavelengths of light. +</p> + +<p> + Below you'll see a list of textures you'll frequently find in a PBR pipeline together with its visual output if supplied to a PBR renderer: +</p> + + <img src="/img/pbr/textures.png" class="clean" alt="Example of how artists author a PBR material with its relevant textures (OpenGL)."/> + +<p> + <strong>Albedo</strong>: the <def>albedo</def> texture specifies for each texel the color of the surface, or the base reflectivity if that texel is metallic. This is largely similar to what we've been using before as a diffuse texture, but all lighting information is extracted from the texture. Diffuse textures often have slight shadows or darkened crevices inside the image which is something you don't want in an albedo texture; it should only contain the color (or refracted absorption coefficients) of the surface. +</p> + +<p> + <strong>Normal</strong>: the normal map texture is exactly as we've been using before in the <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping" target="_blank">normal mapping</a> chapter. The normal map allows us to specify, per fragment, a unique normal to give the illusion that a surface is <em>bumpier</em> than its flat counterpart. +</p> + +<p> + <strong>Metallic</strong>: the metallic map specifies per texel whether a texel is either metallic or it isn't. Based on how the PBR engine is set up, artists can author metalness as either grayscale values or as binary black or white. +</p> + +<p> + <strong>Roughness</strong>: the roughness map specifies how rough a surface is on a per texel basis. The sampled roughness value of the roughness influences the statistical microfacet orientations of the surface. A rougher surface gets wider and blurrier reflections, while a smooth surface gets focused and clear reflections. Some PBR engines expect a <def>smoothness</def> map instead of a roughness map which some artists find more intuitive. These values are then translated (<code>1.0 - smoothness</code>) to roughness the moment they're sampled. +</p> + +<p> + <strong>AO</strong>: the <def>ambient occlusion</def> or <def>AO</def> map specifies an extra shadowing factor of the surface and potentially surrounding geometry. If we have a brick surface for instance, the albedo texture should have no shadowing information inside the brick's crevices. The AO map however does specify these darkened edges as it's more difficult for light to escape. Taking ambient occlusion in account at the end of the lighting stage can significantly boost the visual quality of your scene. The ambient occlusion map of a mesh/surface is either manually generated, or pre-calculated in 3D modeling programs. +</p> + +<p> + Artists set and tweak these physically based input values on a per-texel basis and can base their texture values on the physical surface properties of real-world materials. This is one of the biggest advantages of a PBR render pipeline as these physical properties of a surface remain the same, regardless of environment or lighting setup, making life easier for artists to get physically plausible results. Surfaces authored in a PBR pipeline can easily be shared among different PBR render engines, will look correct regardless of the environment they're in, and as a result look much more natural. +</p> + +<h2>Further reading</h2> +<ul> + <li><a href="http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf" target="_blank">Background: Physics and Math of Shading by Naty Hoffmann</a>: there is too much theory to fully discuss in a single article so the theory here barely scratches the surface; if you want to know more about the physics of light and how it relates to the theory of PBR <strong>this</strong> is the resource you want to read.</li> + <li><a href="http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf" target="_blank">Real shading in Unreal Engine 4</a>: discusses the PBR model adopted by Epic Games in their 4th Unreal Engine installment. The PBR system we'll focus on in these chapters is based on this model of PBR.</li> + <li><a href="https://www.shadertoy.com/view/4sSfzK" target="_blank">[SH17C] Physically Based Shading, by knarkowicz</a>: great showcase of all individual PBR elements in an interactive ShaderToy demo.</li> + <li><a href="https://www.marmoset.co/toolbag/learn/pbr-theory" target="_blank">Marmoset: PBR Theory</a>: an introduction to PBR mostly meant for artists, but nevertheless a good read.</li> + <li><a href="http://www.codinglabs.net/article_physically_based_rendering.aspx" target="_blank">Coding Labs: Physically based rendering</a>: an introduction to the render equation and how it relates to PBR. </li> + <li><a href="http://www.codinglabs.net/article_physically_based_rendering_cook_torrance.aspx" target="_blank">Coding Labs: Physically Based Rendering - Cook–Torrance</a>: an introduction to the Cook-Torrance BRDF. </li> + <li><a href="http://blog.wolfire.com/2015/10/Physically-based-rendering" target="_blank">Wolfire Games - Physically based rendering</a>: an introduction to PBR by Lukas Orsvärn.</li> + <li><a href="https://www.shadertoy.com/view/4sSfzK" target="_blank">[SH17C] Physically Based Shading</a>: a great interactive shadertoy example (warning: may take a while to load) by Krzysztof Narkowi showcasing light-material interaction in a PBR fashion.</li> +</ul> + + + </div> + + </main> +</body> +</html> diff --git a/pub/Translations.html b/pub/Translations.html @@ -0,0 +1,389 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <h1 id="content-title">Translations</h1> +<h1 id="content-url" style='display:none;'>Translations</h1> +<p> + Thanks to a massive effort on the community's part, several translations have been published of LearnOpenGL's content. You'll always get the latest and most up-to-date version here, but if you find it easier to read in your native language the following translations did an amazing job: +</p> + +<img src="/img/flag_china.png" class="translation"/> <strong>Chinese</strong>: + +<ul id="translations"> + <li><a href="https://learnopengl-cn.github.io/" target="_blank">LearnOpenGL CN</a></li> + <!--<li><a href="http://bullteacher.com/category/zh_learnopengl_com" target="_blank">bullteacher.com</a></li>--> +</ul> + +<img src="/img/flag_russian.png" class="translation"/> <strong>Russian</strong>: + +<ul id="translations"> + <li><a href="https://habrahabr.ru/post/336166/" target="_blank">LearnOpenGL</a></li> +</ul> + +<img src="/img/flag_french.png" class="translation"/> <strong>French</strong>: + +<ul id="translations"> + <li><a href=" https://opengl.developpez.com/tutoriels/apprendre-opengl/" target="_blank">OpenGL Developpez</a></li> +</ul> + +<img src="/img/flag_polish.png" class="translation"/> <strong>Polish</strong>: + +<ul id="translations"> + <li><a href="https://shot511.github.io/pages/learnopengl/" target="_blank">Real-time rendering class</a></li> +</ul> + +<img src="/img/flag_korean.png" class="translation"/> <strong>Korean</strong>: + +<ul id="translations"> + <li><a href="http://heinleinsgame.tistory.com/category/OpenGL" target="_blank">Heinleinsgame OpenGL</a></li> + <li><a href="https://gyutts.tistory.com/86?category=755809" target="_blank">Game Developer Q.bot</a></li> +</ul> + +<img src="/img/flag_turkey.png" class="translation"/> <strong>Turkish</strong>: + +<ul id="translations"> + <li><a href="https://cgtranslators.gitbook.io/opengl-ogrenin/" target="_blank">OpenGL' e Hoşgeldiniz</a></li> +</ul> + + + +<p> + If you've been working on a translation of LearnOpenGL and already have a good amount of translated content, let me know and I'll be sure to add a link to your translation above. +</p> + + </div> + + <div id="hover"> + HI + </div> + <!-- 728x90/320x50 sticky footer --> +<div id="waldo-tag-6196"></div> + + <div id="disqus_thread"></div> + + + + +</div> <!-- container div --> + + +</div> <!-- super container div --> +</body> +</html> + </main> +</body> +</html> diff --git a/pub/img/getting-started/camera_axes.png b/pub/img/getting-started/camera_axes.png Binary files differ. diff --git a/pub/img/getting-started/camera_pitch.png b/pub/img/getting-started/camera_pitch.png Binary files differ. diff --git a/pub/img/getting-started/camera_pitch_yaw_roll.png b/pub/img/getting-started/camera_pitch_yaw_roll.png Binary files differ. diff --git a/pub/img/getting-started/camera_triangle.png b/pub/img/getting-started/camera_triangle.png Binary files differ. diff --git a/pub/img/getting-started/camera_yaw.png b/pub/img/getting-started/camera_yaw.png Binary files differ. diff --git a/pub/img/getting-started/cmake.png b/pub/img/getting-started/cmake.png Binary files differ. diff --git a/pub/img/getting-started/coordinate_systems.png b/pub/img/getting-started/coordinate_systems.png Binary files differ. diff --git a/pub/img/getting-started/coordinate_systems_multiple_objects.png b/pub/img/getting-started/coordinate_systems_multiple_objects.png Binary files differ. diff --git a/pub/img/getting-started/coordinate_systems_result.png b/pub/img/getting-started/coordinate_systems_result.png Binary files differ. diff --git a/pub/img/getting-started/coordinate_systems_right_handed.png b/pub/img/getting-started/coordinate_systems_right_handed.png Binary files differ. diff --git a/pub/img/getting-started/filter_linear.png b/pub/img/getting-started/filter_linear.png Binary files differ. diff --git a/pub/img/getting-started/filter_nearest.png b/pub/img/getting-started/filter_nearest.png Binary files differ. diff --git a/pub/img/getting-started/glm.png b/pub/img/getting-started/glm.png Binary files differ. diff --git a/pub/img/getting-started/hellotriangle.png b/pub/img/getting-started/hellotriangle.png Binary files differ. diff --git a/pub/img/getting-started/hellotriangle2.png b/pub/img/getting-started/hellotriangle2.png Binary files differ. diff --git a/pub/img/getting-started/hellowindow.png b/pub/img/getting-started/hellowindow.png Binary files differ. diff --git a/pub/img/getting-started/hellowindow2.png b/pub/img/getting-started/hellowindow2.png Binary files differ. diff --git a/pub/img/getting-started/include_directories.png b/pub/img/getting-started/include_directories.png Binary files differ. diff --git a/pub/img/getting-started/linker_input.png b/pub/img/getting-started/linker_input.png Binary files differ. diff --git a/pub/img/getting-started/matrix_multiplication.png b/pub/img/getting-started/matrix_multiplication.png Binary files differ. diff --git a/pub/img/getting-started/mipmaps.png b/pub/img/getting-started/mipmaps.png Binary files differ. diff --git a/pub/img/getting-started/ndc.png b/pub/img/getting-started/ndc.png Binary files differ. diff --git a/pub/img/getting-started/opengl.jpg b/pub/img/getting-started/opengl.jpg Binary files differ. diff --git a/pub/img/getting-started/orthographic_frustum.png b/pub/img/getting-started/orthographic_frustum.png Binary files differ. diff --git a/pub/img/getting-started/perspective.png b/pub/img/getting-started/perspective.png Binary files differ. diff --git a/pub/img/getting-started/perspective_frustum.png b/pub/img/getting-started/perspective_frustum.png Binary files differ. diff --git a/pub/img/getting-started/perspective_orthographic.png b/pub/img/getting-started/perspective_orthographic.png Binary files differ. diff --git a/pub/img/getting-started/pipeline.png b/pub/img/getting-started/pipeline.png Binary files differ. diff --git a/pub/img/getting-started/shader2.png b/pub/img/getting-started/shader2.png Binary files differ. diff --git a/pub/img/getting-started/shaders.png b/pub/img/getting-started/shaders.png Binary files differ. diff --git a/pub/img/getting-started/shaders3.png b/pub/img/getting-started/shaders3.png Binary files differ. diff --git a/pub/img/getting-started/start_video.png b/pub/img/getting-started/start_video.png Binary files differ. diff --git a/pub/img/getting-started/tex_coords.png b/pub/img/getting-started/tex_coords.png Binary files differ. diff --git a/pub/img/getting-started/texture_filtering.png b/pub/img/getting-started/texture_filtering.png Binary files differ. diff --git a/pub/img/getting-started/texture_wrapping.png b/pub/img/getting-started/texture_wrapping.png Binary files differ. diff --git a/pub/img/getting-started/textures.png b/pub/img/getting-started/textures.png Binary files differ. diff --git a/pub/img/getting-started/textures2.png b/pub/img/getting-started/textures2.png Binary files differ. diff --git a/pub/img/getting-started/textures_combined.png b/pub/img/getting-started/textures_combined.png Binary files differ. diff --git a/pub/img/getting-started/textures_combined2.png b/pub/img/getting-started/textures_combined2.png Binary files differ. diff --git a/pub/img/getting-started/textures_funky.png b/pub/img/getting-started/textures_funky.png Binary files differ. diff --git a/pub/img/getting-started/transformations.png b/pub/img/getting-started/transformations.png Binary files differ. diff --git a/pub/img/getting-started/vc_directories.png b/pub/img/getting-started/vc_directories.png Binary files differ. diff --git a/pub/img/getting-started/vectors.png b/pub/img/getting-started/vectors.png Binary files differ. diff --git a/pub/img/getting-started/vectors_addition.png b/pub/img/getting-started/vectors_addition.png Binary files differ. diff --git a/pub/img/getting-started/vectors_angle.png b/pub/img/getting-started/vectors_angle.png Binary files differ. diff --git a/pub/img/getting-started/vectors_crossproduct.png b/pub/img/getting-started/vectors_crossproduct.png Binary files differ. diff --git a/pub/img/getting-started/vectors_scale.png b/pub/img/getting-started/vectors_scale.png Binary files differ. diff --git a/pub/img/getting-started/vectors_subtraction.png b/pub/img/getting-started/vectors_subtraction.png Binary files differ. diff --git a/pub/img/getting-started/vectors_triangle.png b/pub/img/getting-started/vectors_triangle.png Binary files differ. diff --git a/pub/img/getting-started/vertex_array_objects.png b/pub/img/getting-started/vertex_array_objects.png Binary files differ. diff --git a/pub/img/getting-started/vertex_array_objects_ebo.png b/pub/img/getting-started/vertex_array_objects_ebo.png Binary files differ. diff --git a/pub/img/getting-started/vertex_attribute_pointer.png b/pub/img/getting-started/vertex_attribute_pointer.png Binary files differ. diff --git a/pub/img/getting-started/vertex_attribute_pointer_interleaved.png b/pub/img/getting-started/vertex_attribute_pointer_interleaved.png Binary files differ. diff --git a/pub/img/getting-started/vertex_attribute_pointer_interleaved_textures.png b/pub/img/getting-started/vertex_attribute_pointer_interleaved_textures.png Binary files differ. diff --git a/pub/img/getting-started/x64.png b/pub/img/getting-started/x64.png Binary files differ. diff --git a/pub/img/start_video.png b/pub/img/start_video.png Binary files differ. diff --git a/pub/img/textures/awesomeface.png b/pub/img/textures/awesomeface.png Binary files differ. diff --git a/pub/static/functions.js b/pub/static/functions.js @@ -0,0 +1,7 @@ +function ClickVideo(video){ + if(video.children[0].paused) + video.children[0].play(); + else + video.children[0].pause(); + video.classList.toggle("paused"); +} diff --git a/pub/static/mathjax.js b/pub/static/mathjax.js @@ -0,0 +1,19 @@ +/* + * /MathJax.js + * + * Copyright (c) 2009-2015 The MathJax Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if(document.getElementById&&document.childNodes&&document.createElement){if(!(window.MathJax&&MathJax.Hub)){if(window.MathJax){window.MathJax={AuthorConfig:window.MathJax}}else{window.MathJax={}}MathJax.isPacked=true;MathJax.version="2.6.1";MathJax.fileversion="2.6.1";MathJax.cdnVersion="2.6.1";MathJax.cdnFileVersions={};(function(d){var b=window[d];if(!b){b=window[d]={}}var e=[];var c=function(f){var g=f.constructor;if(!g){g=function(){}}for(var h in f){if(h!=="constructor"&&f.hasOwnProperty(h)){g[h]=f[h]}}return g};var a=function(){return function(){return arguments.callee.Init.call(this,arguments)}};b.Object=c({constructor:a(),Subclass:function(f,h){var g=a();g.SUPER=this;g.Init=this.Init;g.Subclass=this.Subclass;g.Augment=this.Augment;g.protoFunction=this.protoFunction;g.can=this.can;g.has=this.has;g.isa=this.isa;g.prototype=new this(e);g.prototype.constructor=g;g.Augment(f,h);return g},Init:function(f){var g=this;if(f.length===1&&f[0]===e){return g}if(!(g instanceof f.callee)){g=new f.callee(e)}return g.Init.apply(g,f)||g},Augment:function(f,g){var h;if(f!=null){for(h in f){if(f.hasOwnProperty(h)){this.protoFunction(h,f[h])}}if(f.toString!==this.prototype.toString&&f.toString!=={}.toString){this.protoFunction("toString",f.toString)}}if(g!=null){for(h in g){if(g.hasOwnProperty(h)){this[h]=g[h]}}}return this},protoFunction:function(g,f){this.prototype[g]=f;if(typeof f==="function"){f.SUPER=this.SUPER.prototype}},prototype:{Init:function(){},SUPER:function(f){return f.callee.SUPER},can:function(f){return typeof(this[f])==="function"},has:function(f){return typeof(this[f])!=="undefined"},isa:function(f){return(f instanceof Object)&&(this instanceof f)}},can:function(f){return this.prototype.can.call(this,f)},has:function(f){return this.prototype.has.call(this,f)},isa:function(g){var f=this;while(f){if(f===g){return true}else{f=f.SUPER}}return false},SimpleSUPER:c({constructor:function(f){return this.SimpleSUPER.define(f)},define:function(f){var h={};if(f!=null){for(var g in f){if(f.hasOwnProperty(g)){h[g]=this.wrap(g,f[g])}}if(f.toString!==this.prototype.toString&&f.toString!=={}.toString){h.toString=this.wrap("toString",f.toString)}}return h},wrap:function(i,h){if(typeof(h)!=="function"||!h.toString().match(/\.\s*SUPER\s*\(/)){return h}var g=function(){this.SUPER=g.SUPER[i];try{var f=h.apply(this,arguments)}catch(j){delete this.SUPER;throw j}delete this.SUPER;return f};g.toString=function(){return h.toString.apply(h,arguments)};return g}})})})("MathJax");(function(BASENAME){var BASE=window[BASENAME];if(!BASE){BASE=window[BASENAME]={}}var CALLBACK=function(data){var cb=function(){return arguments.callee.execute.apply(arguments.callee,arguments)};for(var id in CALLBACK.prototype){if(CALLBACK.prototype.hasOwnProperty(id)){if(typeof(data[id])!=="undefined"){cb[id]=data[id]}else{cb[id]=CALLBACK.prototype[id]}}}cb.toString=CALLBACK.prototype.toString;return cb};CALLBACK.prototype={isCallback:true,hook:function(){},data:[],object:window,execute:function(){if(!this.called||this.autoReset){this.called=!this.autoReset;return this.hook.apply(this.object,this.data.concat([].slice.call(arguments,0)))}},reset:function(){delete this.called},toString:function(){return this.hook.toString.apply(this.hook,arguments)}};var ISCALLBACK=function(f){return(typeof(f)==="function"&&f.isCallback)};var EVAL=function(code){return eval.call(window,code)};var TESTEVAL=function(){EVAL("var __TeSt_VaR__ = 1");if(window.__TeSt_VaR__){try{delete window.__TeSt_VaR__}catch(error){window.__TeSt_VaR__=null}}else{if(window.execScript){EVAL=function(code){BASE.__code=code;code="try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";window.execScript(code);var result=BASE.__result;delete BASE.__result;delete BASE.__code;if(result instanceof Error){throw result}return result}}else{EVAL=function(code){BASE.__code=code;code="try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";var head=(document.getElementsByTagName("head"))[0];if(!head){head=document.body}var script=document.createElement("script");script.appendChild(document.createTextNode(code));head.appendChild(script);head.removeChild(script);var result=BASE.__result;delete BASE.__result;delete BASE.__code;if(result instanceof Error){throw result}return result}}}TESTEVAL=null};var USING=function(args,i){if(arguments.length>1){if(arguments.length===2&&!(typeof arguments[0]==="function")&&arguments[0] instanceof Object&&typeof arguments[1]==="number"){args=[].slice.call(args,i)}else{args=[].slice.call(arguments,0)}}if(args instanceof Array&&args.length===1){args=args[0]}if(typeof args==="function"){if(args.execute===CALLBACK.prototype.execute){return args}return CALLBACK({hook:args})}else{if(args instanceof Array){if(typeof(args[0])==="string"&&args[1] instanceof Object&&typeof args[1][args[0]]==="function"){return CALLBACK({hook:args[1][args[0]],object:args[1],data:args.slice(2)})}else{if(typeof args[0]==="function"){return CALLBACK({hook:args[0],data:args.slice(1)})}else{if(typeof args[1]==="function"){return CALLBACK({hook:args[1],object:args[0],data:args.slice(2)})}}}}else{if(typeof(args)==="string"){if(TESTEVAL){TESTEVAL()}return CALLBACK({hook:EVAL,data:[args]})}else{if(args instanceof Object){return CALLBACK(args)}else{if(typeof(args)==="undefined"){return CALLBACK({})}}}}}throw Error("Can't make callback from given data")};var DELAY=function(time,callback){callback=USING(callback);callback.timeout=setTimeout(callback,time);return callback};var WAITFOR=function(callback,signal){callback=USING(callback);if(!callback.called){WAITSIGNAL(callback,signal);signal.pending++}};var WAITEXECUTE=function(){var signals=this.signal;delete this.signal;this.execute=this.oldExecute;delete this.oldExecute;var result=this.execute.apply(this,arguments);if(ISCALLBACK(result)&&!result.called){WAITSIGNAL(result,signals)}else{for(var i=0,m=signals.length;i<m;i++){signals[i].pending--;if(signals[i].pending<=0){signals[i].call()}}}};var WAITSIGNAL=function(callback,signals){if(!(signals instanceof Array)){signals=[signals]}if(!callback.signal){callback.oldExecute=callback.execute;callback.execute=WAITEXECUTE;callback.signal=signals}else{if(signals.length===1){callback.signal.push(signals[0])}else{callback.signal=callback.signal.concat(signals)}}};var AFTER=function(callback){callback=USING(callback);callback.pending=0;for(var i=1,m=arguments.length;i<m;i++){if(arguments[i]){WAITFOR(arguments[i],callback)}}if(callback.pending===0){var result=callback();if(ISCALLBACK(result)){callback=result}}return callback};var HOOKS=MathJax.Object.Subclass({Init:function(reset){this.hooks=[];this.remove=[];this.reset=reset;this.running=false},Add:function(hook,priority){if(priority==null){priority=10}if(!ISCALLBACK(hook)){hook=USING(hook)}hook.priority=priority;var i=this.hooks.length;while(i>0&&priority<this.hooks[i-1].priority){i--}this.hooks.splice(i,0,hook);return hook},Remove:function(hook){for(var i=0,m=this.hooks.length;i<m;i++){if(this.hooks[i]===hook){if(this.running){this.remove.push(i)}else{this.hooks.splice(i,1)}return}}},Execute:function(){var callbacks=[{}];this.running=true;for(var i=0,m=this.hooks.length;i<m;i++){if(this.reset){this.hooks[i].reset()}var result=this.hooks[i].apply(window,arguments);if(ISCALLBACK(result)&&!result.called){callbacks.push(result)}}this.running=false;if(this.remove.length){this.RemovePending()}if(callbacks.length===1){return null}if(callbacks.length===2){return callbacks[1]}return AFTER.apply({},callbacks)},RemovePending:function(){this.remove=this.remove.sort();for(var i=this.remove.length-1;i>=0;i--){this.hooks.splice(i,1)}this.remove=[]}});var EXECUTEHOOKS=function(hooks,data,reset){if(!hooks){return null}if(!(hooks instanceof Array)){hooks=[hooks]}if(!(data instanceof Array)){data=(data==null?[]:[data])}var handler=HOOKS(reset);for(var i=0,m=hooks.length;i<m;i++){handler.Add(hooks[i])}return handler.Execute.apply(handler,data)};var QUEUE=BASE.Object.Subclass({Init:function(){this.pending=this.running=0;this.queue=[];this.Push.apply(this,arguments)},Push:function(){var callback;for(var i=0,m=arguments.length;i<m;i++){callback=USING(arguments[i]);if(callback===arguments[i]&&!callback.called){callback=USING(["wait",this,callback])}this.queue.push(callback)}if(!this.running&&!this.pending){this.Process()}return callback},Process:function(queue){while(!this.running&&!this.pending&&this.queue.length){var callback=this.queue[0];queue=this.queue.slice(1);this.queue=[];this.Suspend();var result=callback();this.Resume();if(queue.length){this.queue=queue.concat(this.queue)}if(ISCALLBACK(result)&&!result.called){WAITFOR(result,this)}}},Suspend:function(){this.running++},Resume:function(){if(this.running){this.running--}},call:function(){this.Process.apply(this,arguments)},wait:function(callback){return callback}});var SIGNAL=QUEUE.Subclass({Init:function(name){QUEUE.prototype.Init.call(this);this.name=name;this.posted=[];this.listeners=HOOKS(true);this.posting=false;this.callback=null},Post:function(message,callback,forget){callback=USING(callback);if(this.posting||this.pending){this.Push(["Post",this,message,callback,forget])}else{this.callback=callback;callback.reset();if(!forget){this.posted.push(message)}this.Suspend();this.posting=true;var result=this.listeners.Execute(message);if(ISCALLBACK(result)&&!result.called){WAITFOR(result,this)}this.Resume();this.posting=false;if(!this.pending){this.call()}}return callback},Clear:function(callback){callback=USING(callback);if(this.posting||this.pending){callback=this.Push(["Clear",this,callback])}else{this.posted=[];callback()}return callback},call:function(){this.callback(this);this.Process()},Interest:function(callback,ignorePast,priority){callback=USING(callback);this.listeners.Add(callback,priority);if(!ignorePast){for(var i=0,m=this.posted.length;i<m;i++){callback.reset();var result=callback(this.posted[i]);if(ISCALLBACK(result)&&i===this.posted.length-1){WAITFOR(result,this)}}}return callback},NoInterest:function(callback){this.listeners.Remove(callback)},MessageHook:function(msg,callback,priority){callback=USING(callback);if(!this.hooks){this.hooks={};this.Interest(["ExecuteHooks",this])}if(!this.hooks[msg]){this.hooks[msg]=HOOKS(true)}this.hooks[msg].Add(callback,priority);for(var i=0,m=this.posted.length;i<m;i++){if(this.posted[i]==msg){callback.reset();callback(this.posted[i])}}callback.msg=msg;return callback},ExecuteHooks:function(msg){var type=((msg instanceof Array)?msg[0]:msg);if(!this.hooks[type]){return null}return this.hooks[type].Execute(msg)},RemoveHook:function(hook){this.hooks[hook.msg].Remove(hook)}},{signals:{},find:function(name){if(!SIGNAL.signals[name]){SIGNAL.signals[name]=new SIGNAL(name)}return SIGNAL.signals[name]}});BASE.Callback=BASE.CallBack=USING;BASE.Callback.Delay=DELAY;BASE.Callback.After=AFTER;BASE.Callback.Queue=QUEUE;BASE.Callback.Signal=SIGNAL.find;BASE.Callback.Hooks=HOOKS;BASE.Callback.ExecuteHooks=EXECUTEHOOKS})("MathJax");(function(e){var a=window[e];if(!a){a=window[e]={}}var d=(navigator.vendor==="Apple Computer, Inc."&&typeof navigator.vendorSub==="undefined");var g=0;var h=function(i){if(document.styleSheets&&document.styleSheets.length>g){g=document.styleSheets.length}if(!i){i=document.head||((document.getElementsByTagName("head"))[0]);if(!i){i=document.body}}return i};var f=[];var c=function(){for(var k=0,j=f.length;k<j;k++){a.Ajax.head.removeChild(f[k])}f=[]};var b={};b[e]="";a.Ajax={loaded:{},loading:{},loadHooks:{},timeout:15*1000,styleDelay:1,config:{root:"",path:b},STATUS:{OK:1,ERROR:-1},fileURL:function(j){var i=j.match(/^\[([-._a-z0-9]+)\]/i);if(i&&i[1] in b){j=(b[i[1]]||this.config.root)+j.substr(i[1].length+2)}return j},fileName:function(j){var i=this.config.root;if(j.substr(0,i.length)===i){j="["+e+"]"+j.substr(i.length)}else{for(var k in b){if(b.hasOwnProperty(k)&&b[k]){if(j.substr(0,b[k].length)===b[k]){j="["+k+"]"+j.substr(b[k].length);break}}}}return j},fileRev:function(j){var i=a.cdnFileVersions[name]||a.cdnVersion;if(i){i="?rev="+i}return i},urlRev:function(i){return this.fileURL(i)+this.fileRev(i)},Require:function(k,n){n=a.Callback(n);var l;if(k instanceof Object){for(var j in k){if(k.hasOwnProperty(j)){l=j.toUpperCase();k=k[j]}}}else{l=k.split(/\./).pop().toUpperCase()}k=this.fileURL(k);if(this.loaded[k]){n(this.loaded[k])}else{var m={};m[l]=k;this.Load(m,n)}return n},Load:function(k,m){m=a.Callback(m);var l;if(k instanceof Object){for(var j in k){if(k.hasOwnProperty(j)){l=j.toUpperCase();k=k[j]}}}else{l=k.split(/\./).pop().toUpperCase()}k=this.fileURL(k);if(this.loading[k]){this.addHook(k,m)}else{this.head=h(this.head);if(this.loader[l]){this.loader[l].call(this,k,m)}else{throw Error("Can't load files of type "+l)}}return m},LoadHook:function(l,m,k){m=a.Callback(m);if(l instanceof Object){for(var j in l){if(l.hasOwnProperty(j)){l=l[j]}}}l=this.fileURL(l);if(this.loaded[l]){m(this.loaded[l])}else{this.addHook(l,m,k)}return m},addHook:function(j,k,i){if(!this.loadHooks[j]){this.loadHooks[j]=MathJax.Callback.Hooks()}this.loadHooks[j].Add(k,i);k.file=j},removeHook:function(i){if(this.loadHooks[i.file]){this.loadHooks[i.file].Remove(i);if(!this.loadHooks[i.file].hooks.length){delete this.loadHooks[i.file]}}},Preloading:function(){for(var l=0,j=arguments.length;l<j;l++){var k=this.fileURL(arguments[l]);if(!this.loading[k]){this.loading[k]={preloaded:true}}}},loader:{JS:function(k,m){var j=this.fileName(k);var i=document.createElement("script");var l=a.Callback(["loadTimeout",this,k]);this.loading[k]={callback:m,timeout:setTimeout(l,this.timeout),status:this.STATUS.OK,script:i};this.loading[k].message=a.Message.File(j);i.onerror=l;i.type="text/javascript";i.src=k+this.fileRev(j);this.head.appendChild(i)},CSS:function(j,l){var i=this.fileName(j);var k=document.createElement("link");k.rel="stylesheet";k.type="text/css";k.href=j+this.fileRev(i);this.loading[j]={callback:l,message:a.Message.File(i),status:this.STATUS.OK};this.head.appendChild(k);this.timer.create.call(this,[this.timer.file,j],k)}},timer:{create:function(j,i){j=a.Callback(j);if(i.nodeName==="STYLE"&&i.styleSheet&&typeof(i.styleSheet.cssText)!=="undefined"){j(this.STATUS.OK)}else{if(window.chrome&&i.nodeName==="LINK"){j(this.STATUS.OK)}else{if(d){this.timer.start(this,[this.timer.checkSafari2,g++,j],this.styleDelay)}else{this.timer.start(this,[this.timer.checkLength,i,j],this.styleDelay)}}}return j},start:function(j,i,k,l){i=a.Callback(i);i.execute=this.execute;i.time=this.time;i.STATUS=j.STATUS;i.timeout=l||j.timeout;i.delay=i.total=k||0;if(k){setTimeout(i,k)}else{i()}},time:function(i){this.total+=this.delay;this.delay=Math.floor(this.delay*1.05+5);if(this.total>=this.timeout){i(this.STATUS.ERROR);return 1}return 0},file:function(j,i){if(i<0){a.Ajax.loadTimeout(j)}else{a.Ajax.loadComplete(j)}},execute:function(){this.hook.call(this.object,this,this.data[0],this.data[1])},checkSafari2:function(i,j,k){if(i.time(k)){return}if(document.styleSheets.length>j&&document.styleSheets[j].cssRules&&document.styleSheets[j].cssRules.length){k(i.STATUS.OK)}else{setTimeout(i,i.delay)}},checkLength:function(i,l,n){if(i.time(n)){return}var m=0;var j=(l.sheet||l.styleSheet);try{if((j.cssRules||j.rules||[]).length>0){m=1}}catch(k){if(k.message.match(/protected variable|restricted URI/)){m=1}else{if(k.message.match(/Security error/)){m=1}}}if(m){setTimeout(a.Callback([n,i.STATUS.OK]),0)}else{setTimeout(i,i.delay)}}},loadComplete:function(i){i=this.fileURL(i);var j=this.loading[i];if(j&&!j.preloaded){a.Message.Clear(j.message);clearTimeout(j.timeout);if(j.script){if(f.length===0){setTimeout(c,0)}f.push(j.script)}this.loaded[i]=j.status;delete this.loading[i];this.addHook(i,j.callback)}else{if(j){delete this.loading[i]}this.loaded[i]=this.STATUS.OK;j={status:this.STATUS.OK}}if(!this.loadHooks[i]){return null}return this.loadHooks[i].Execute(j.status)},loadTimeout:function(i){if(this.loading[i].timeout){clearTimeout(this.loading[i].timeout)}this.loading[i].status=this.STATUS.ERROR;this.loadError(i);this.loadComplete(i)},loadError:function(i){a.Message.Set(["LoadFailed","File failed to load: %1",i],null,2000);a.Hub.signal.Post(["file load error",i])},Styles:function(k,l){var i=this.StyleString(k);if(i===""){l=a.Callback(l);l()}else{var j=document.createElement("style");j.type="text/css";this.head=h(this.head);this.head.appendChild(j);if(j.styleSheet&&typeof(j.styleSheet.cssText)!=="undefined"){j.styleSheet.cssText=i}else{j.appendChild(document.createTextNode(i))}l=this.timer.create.call(this,l,j)}return l},StyleString:function(n){if(typeof(n)==="string"){return n}var k="",o,m;for(o in n){if(n.hasOwnProperty(o)){if(typeof n[o]==="string"){k+=o+" {"+n[o]+"}\n"}else{if(n[o] instanceof Array){for(var l=0;l<n[o].length;l++){m={};m[o]=n[o][l];k+=this.StyleString(m)}}else{if(o.substr(0,6)==="@media"){k+=o+" {"+this.StyleString(n[o])+"}\n"}else{if(n[o]!=null){m=[];for(var j in n[o]){if(n[o].hasOwnProperty(j)){if(n[o][j]!=null){m[m.length]=j+": "+n[o][j]}}}k+=o+" {"+m.join("; ")+"}\n"}}}}}}return k}}})("MathJax");MathJax.HTML={Element:function(d,f,e){var g=document.createElement(d),h;if(f){if(f.hasOwnProperty("style")){var c=f.style;f.style={};for(h in c){if(c.hasOwnProperty(h)){f.style[h.replace(/-([a-z])/g,this.ucMatch)]=c[h]}}}MathJax.Hub.Insert(g,f);for(h in f){if(h==="role"||h.substr(0,5)==="aria-"){g.setAttribute(h,f[h])}}}if(e){if(!(e instanceof Array)){e=[e]}for(var b=0,a=e.length;b<a;b++){if(e[b] instanceof Array){g.appendChild(this.Element(e[b][0],e[b][1],e[b][2]))}else{if(d==="script"){this.setScript(g,e[b])}else{g.appendChild(document.createTextNode(e[b]))}}}}return g},ucMatch:function(a,b){return b.toUpperCase()},addElement:function(b,a,d,c){return b.appendChild(this.Element(a,d,c))},TextNode:function(a){return document.createTextNode(a)},addText:function(a,b){return a.appendChild(this.TextNode(b))},setScript:function(a,b){if(this.setScriptBug){a.text=b}else{while(a.firstChild){a.removeChild(a.firstChild)}this.addText(a,b)}},getScript:function(a){var b=(a.text===""?a.innerHTML:a.text);return b.replace(/^\s+/,"").replace(/\s+$/,"")},Cookie:{prefix:"mjx",expires:365,Set:function(a,e){var d=[];if(e){for(var g in e){if(e.hasOwnProperty(g)){d.push(g+":"+e[g].toString().replace(/&/g,"&&"))}}}var b=this.prefix+"."+a+"="+escape(d.join("&;"));if(this.expires){var f=new Date();f.setDate(f.getDate()+this.expires);b+="; expires="+f.toGMTString()}try{document.cookie=b+"; path=/"}catch(c){}},Get:function(a,d){if(!d){d={}}var g=new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+a+"=([^;]*)(?:;|$)");var f;try{f=g.exec(document.cookie)}catch(c){}if(f&&f[1]!==""){var j=unescape(f[1]).split("&;");for(var e=0,b=j.length;e<b;e++){f=j[e].match(/([^:]+):(.*)/);var h=f[2].replace(/&&/g,"&");if(h==="true"){h=true}else{if(h==="false"){h=false}else{if(h.match(/^-?(\d+(\.\d+)?|\.\d+)$/)){h=parseFloat(h)}}}d[f[1]]=h}}return d}}};MathJax.Localization={locale:"en",directory:"[MathJax]/localization",strings:{ast:{menuTitle:"asturianu"},bg:{menuTitle:"\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438"},bcc:{menuTitle:"\u0628\u0644\u0648\u0686\u06CC"},br:{menuTitle:"brezhoneg"},ca:{menuTitle:"catal\u00E0"},cdo:{menuTitle:"M\u00ECng-d\u0115\u0324ng-ng\u1E73\u0304"},cs:{menuTitle:"\u010De\u0161tina"},da:{menuTitle:"dansk"},de:{menuTitle:"Deutsch"},en:{menuTitle:"English",isLoaded:true},eo:{menuTitle:"Esperanto"},es:{menuTitle:"espa\u00F1ol"},fa:{menuTitle:"\u0641\u0627\u0631\u0633\u06CC"},fi:{menuTitle:"suomi"},fr:{menuTitle:"fran\u00E7ais"},gl:{menuTitle:"galego"},he:{menuTitle:"\u05E2\u05D1\u05E8\u05D9\u05EA"},ia:{menuTitle:"interlingua"},it:{menuTitle:"italiano"},ja:{menuTitle:"\u65E5\u672C\u8A9E"},kn:{menuTitle:"\u0C95\u0CA8\u0CCD\u0CA8\u0CA1"},ko:{menuTitle:"\uD55C\uAD6D\uC5B4"},lb:{menuTitle:"L\u00EBtzebuergesch"},lt:{menuTitle:"lietuvi\u0173"},mk:{menuTitle:"\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438"},nl:{menuTitle:"Nederlands"},oc:{menuTitle:"occitan"},pl:{menuTitle:"polski"},pt:{menuTitle:"portugus\u00EA"},"pt-br":{menuTitle:"portugu\u00EAs do Brasil"},ru:{menuTitle:"\u0440\u0443\u0441\u0441\u043A\u0438\u0439"},sco:{menuTitle:"Scots"},scn:{menuTitle:"sicilianu"},sl:{menuTitle:"sloven\u0161\u010Dina"},sv:{menuTitle:"svenska"},tr:{menuTitle:"T\u00FCrk\u00E7e"},uk:{menuTitle:"\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"},vi:{menuTitle:"Ti\u1EBFng Vi\u1EC7t"},"zh-hans":{menuTitle:"\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09"}},pattern:/%(\d+|\{\d+\}|\{[a-z]+:\%\d+(?:\|(?:%\{\d+\}|%.|[^\}])*)+\}|.)/g,SPLIT:("axb".split(/(x)/).length===3?function(a,b){return a.split(b)}:function(c,e){var a=[],b,d=0;e.lastIndex=0;while((b=e.exec(c))){a.push(c.substr(d,b.index-d));a.push.apply(a,b.slice(1));d=b.index+b[0].length}a.push(c.substr(d));return a}),_:function(b,a){if(a instanceof Array){return this.processSnippet(b,a)}return this.processString(this.lookupPhrase(b,a),[].slice.call(arguments,2))},processString:function(l,o,g){var j,e;for(j=0,e=o.length;j<e;j++){if(g&&o[j] instanceof Array){o[j]=this.processSnippet(g,o[j])}}var f=this.SPLIT(l,this.pattern);for(j=1,e=f.length;j<e;j+=2){var p=f[j].charAt(0);if(p>="0"&&p<="9"){f[j]=o[f[j]-1];if(typeof f[j]==="number"){f[j]=this.number(f[j])}}else{if(p==="{"){p=f[j].substr(1);if(p>="0"&&p<="9"){f[j]=o[f[j].substr(1,f[j].length-2)-1];if(typeof f[j]==="number"){f[j]=this.number(f[j])}}else{var k=f[j].match(/^\{([a-z]+):%(\d+)\|(.*)\}$/);if(k){if(k[1]==="plural"){var d=o[k[2]-1];if(typeof d==="undefined"){f[j]="???"}else{d=this.plural(d)-1;var h=k[3].replace(/(^|[^%])(%%)*%\|/g,"$1$2%\uEFEF").split(/\|/);if(d>=0&&d<h.length){f[j]=this.processString(h[d].replace(/\uEFEF/g,"|"),o,g)}else{f[j]="???"}}}else{f[j]="%"+f[j]}}}}}if(f[j]==null){f[j]="???"}}if(!g){return f.join("")}var a=[],b="";for(j=0;j<e;j++){b+=f[j];j++;if(j<e){if(f[j] instanceof Array){a.push(b);a=a.concat(f[j]);b=""}else{b+=f[j]}}}if(b!==""){a.push(b)}return a},processSnippet:function(g,e){var c=[];for(var d=0,b=e.length;d<b;d++){if(e[d] instanceof Array){var f=e[d];if(typeof f[1]==="string"){var h=f[0];if(!(h instanceof Array)){h=[g,h]}var a=this.lookupPhrase(h,f[1]);c=c.concat(this.processMarkdown(a,f.slice(2),g))}else{if(f[1] instanceof Array){c=c.concat(this.processSnippet.apply(this,f))}else{if(f.length>=3){c.push([f[0],f[1],this.processSnippet(g,f[2])])}else{c.push(e[d])}}}}else{c.push(e[d])}}return c},markdownPattern:/(%.)|(\*{1,3})((?:%.|.)+?)\2|(`+)((?:%.|.)+?)\4|\[((?:%.|.)+?)\]\(([^\s\)]+)\)/,processMarkdown:function(b,h,d){var j=[],e;var c=b.split(this.markdownPattern);var g=c[0];for(var f=1,a=c.length;f<a;f+=8){if(c[f+1]){e=this.processString(c[f+2],h,d);if(!(e instanceof Array)){e=[e]}e=[["b","i","i"][c[f+1].length-1],{},e];if(c[f+1].length===3){e=["b",{},e]}}else{if(c[f+3]){e=this.processString(c[f+4].replace(/^\s/,"").replace(/\s$/,""),h,d);if(!(e instanceof Array)){e=[e]}e=["code",{},e]}else{if(c[f+5]){e=this.processString(c[f+5],h,d);if(!(e instanceof Array)){e=[e]}e=["a",{href:this.processString(c[f+6],h),target:"_blank"},e]}else{g+=c[f];e=null}}}if(e){j=this.concatString(j,g,h,d);j.push(e);g=""}if(c[f+7]!==""){g+=c[f+7]}}j=this.concatString(j,g,h,d);return j},concatString:function(a,c,b,d){if(c!=""){c=this.processString(c,b,d);if(!(c instanceof Array)){c=[c]}a=a.concat(c)}return a},lookupPhrase:function(f,a,d){if(!d){d="_"}if(f instanceof Array){d=(f[0]||"_");f=(f[1]||"")}var c=this.loadDomain(d);if(c){MathJax.Hub.RestartAfter(c)}var b=this.strings[this.locale];if(b){if(b.domains&&d in b.domains){var e=b.domains[d];if(e.strings&&f in e.strings){a=e.strings[f]}}}return a},loadFile:function(b,d,e){e=MathJax.Callback(e);b=(d.file||b);if(!b.match(/\.js$/)){b+=".js"}if(!b.match(/^([a-z]+:|\[MathJax\])/)){var a=(this.strings[this.locale].directory||this.directory+"/"+this.locale||"[MathJax]/localization/"+this.locale);b=a+"/"+b}var c=MathJax.Ajax.Require(b,function(){d.isLoaded=true;return e()});return(c.called?null:c)},loadDomain:function(c,e){var b,a=this.strings[this.locale];if(a){if(!a.isLoaded){b=this.loadFile(this.locale,a);if(b){return MathJax.Callback.Queue(b,["loadDomain",this,c]).Push(e||{})}}if(a.domains&&c in a.domains){var d=a.domains[c];if(!d.isLoaded){b=this.loadFile(c,d);if(b){return MathJax.Callback.Queue(b).Push(e)}}}}return MathJax.Callback(e)()},Try:function(a){a=MathJax.Callback(a);a.autoReset=true;try{a()}catch(b){if(!b.restart){throw b}MathJax.Callback.After(["Try",this,a],b.restart)}},resetLocale:function(a){if(!a){return}a=a.toLowerCase();while(!this.strings[a]){var c=a.lastIndexOf("-");if(c===-1){return}a=a.substring(0,c)}var b=this.strings[a].remap;this.locale=b?b:a},setLocale:function(a){this.resetLocale(a);if(MathJax.Menu){this.loadDomain("MathMenu")}},addTranslation:function(b,e,c){var d=this.strings[b],a=false;if(!d){d=this.strings[b]={};a=true}if(!d.domains){d.domains={}}if(e){if(!d.domains[e]){d.domains[e]={}}d=d.domains[e]}MathJax.Hub.Insert(d,c);if(a&&MathJax.Menu.menu){MathJax.Menu.CreateLocaleMenu()}},setCSS:function(b){var a=this.strings[this.locale];if(a){if(a.fontFamily){b.style.fontFamily=a.fontFamily}if(a.fontDirection){b.style.direction=a.fontDirection;if(a.fontDirection==="rtl"){b.style.textAlign="right"}}}return b},fontFamily:function(){var a=this.strings[this.locale];return(a?a.fontFamily:null)},fontDirection:function(){var a=this.strings[this.locale];return(a?a.fontDirection:null)},plural:function(b){var a=this.strings[this.locale];if(a&&a.plural){return a.plural(b)}if(b==1){return 1}return 2},number:function(b){var a=this.strings[this.locale];if(a&&a.number){return a.number(b)}return b}};MathJax.Message={ready:false,log:[{}],current:null,textNodeBug:(navigator.vendor==="Apple Computer, Inc."&&typeof navigator.vendorSub==="undefined")||(window.hasOwnProperty&&window.hasOwnProperty("konqueror")),styles:{"#MathJax_Message":{position:"fixed",left:"1px",bottom:"2px","background-color":"#E6E6E6",border:"1px solid #959595",margin:"0px",padding:"2px 8px","z-index":"102",color:"black","font-size":"80%",width:"auto","white-space":"nowrap"},"#MathJax_MSIE_Frame":{position:"absolute",top:0,left:0,width:"0px","z-index":101,border:"0px",margin:"0px",padding:"0px"}},browsers:{MSIE:function(a){MathJax.Message.msieFixedPositionBug=((document.documentMode||0)<7);if(MathJax.Message.msieFixedPositionBug){MathJax.Hub.config.styles["#MathJax_Message"].position="absolute"}MathJax.Message.quirks=(document.compatMode==="BackCompat")},Chrome:function(a){MathJax.Hub.config.styles["#MathJax_Message"].bottom="1.5em";MathJax.Hub.config.styles["#MathJax_Message"].left="1em"}},Init:function(a){if(a){this.ready=true}if(!document.body||!this.ready){return false}if(this.div&&this.div.parentNode==null){this.div=document.getElementById("MathJax_Message");if(this.div){this.text=this.div.firstChild}}if(!this.div){var b=document.body;if(this.msieFixedPositionBug&&window.attachEvent){b=this.frame=this.addDiv(document.body);b.removeAttribute("id");b.style.position="absolute";b.style.border=b.style.margin=b.style.padding="0px";b.style.zIndex="101";b.style.height="0px";b=this.addDiv(b);b.id="MathJax_MSIE_Frame";window.attachEvent("onscroll",this.MoveFrame);window.attachEvent("onresize",this.MoveFrame);this.MoveFrame()}this.div=this.addDiv(b);this.div.style.display="none";this.text=this.div.appendChild(document.createTextNode(""))}return true},addDiv:function(a){var b=document.createElement("div");b.id="MathJax_Message";if(a.firstChild){a.insertBefore(b,a.firstChild)}else{a.appendChild(b)}return b},MoveFrame:function(){var a=(MathJax.Message.quirks?document.body:document.documentElement);var b=MathJax.Message.frame;b.style.left=a.scrollLeft+"px";b.style.top=a.scrollTop+"px";b.style.width=a.clientWidth+"px";b=b.firstChild;b.style.height=a.clientHeight+"px"},localize:function(a){return MathJax.Localization._(a,a)},filterText:function(a,c,b){if(MathJax.Hub.config.messageStyle==="simple"){if(b==="LoadFile"){if(!this.loading){this.loading=this.localize("Loading")+" "}a=this.loading;this.loading+="."}else{if(b==="ProcessMath"){if(!this.processing){this.processing=this.localize("Processing")+" "}a=this.processing;this.processing+="."}else{if(b==="TypesetMath"){if(!this.typesetting){this.typesetting=this.localize("Typesetting")+" "}a=this.typesetting;this.typesetting+="."}}}}return a},Set:function(c,e,b){if(e==null){e=this.log.length;this.log[e]={}}var d="";if(c instanceof Array){d=c[0];if(d instanceof Array){d=d[1]}try{c=MathJax.Localization._.apply(MathJax.Localization,c)}catch(a){if(!a.restart){throw a}if(!a.restart.called){if(this.log[e].restarted==null){this.log[e].restarted=0}this.log[e].restarted++;delete this.log[e].cleared;MathJax.Callback.After(["Set",this,c,e,b],a.restart);return e}}}if(this.timer){clearTimeout(this.timer);delete this.timer}this.log[e].text=c;this.log[e].filteredText=c=this.filterText(c,e,d);if(typeof(this.log[e].next)==="undefined"){this.log[e].next=this.current;if(this.current!=null){this.log[this.current].prev=e}this.current=e}if(this.current===e&&MathJax.Hub.config.messageStyle!=="none"){if(this.Init()){if(this.textNodeBug){this.div.innerHTML=c}else{this.text.nodeValue=c}this.div.style.display="";if(this.status){window.status="";delete this.status}}else{window.status=c;this.status=true}}if(this.log[e].restarted){if(this.log[e].cleared){b=0}if(--this.log[e].restarted===0){delete this.log[e].cleared}}if(b){setTimeout(MathJax.Callback(["Clear",this,e]),b)}else{if(b==0){this.Clear(e,0)}}return e},Clear:function(b,a){if(this.log[b].prev!=null){this.log[this.log[b].prev].next=this.log[b].next}if(this.log[b].next!=null){this.log[this.log[b].next].prev=this.log[b].prev}if(this.current===b){this.current=this.log[b].next;if(this.text){if(this.div.parentNode==null){this.Init()}if(this.current==null){if(this.timer){clearTimeout(this.timer);delete this.timer}if(a==null){a=600}if(a===0){this.Remove()}else{this.timer=setTimeout(MathJax.Callback(["Remove",this]),a)}}else{if(MathJax.Hub.config.messageStyle!=="none"){if(this.textNodeBug){this.div.innerHTML=this.log[this.current].filteredText}else{this.text.nodeValue=this.log[this.current].filteredText}}}if(this.status){window.status="";delete this.status}}else{if(this.status){window.status=(this.current==null?"":this.log[this.current].text)}}}delete this.log[b].next;delete this.log[b].prev;delete this.log[b].filteredText;if(this.log[b].restarted){this.log[b].cleared=true}},Remove:function(){this.text.nodeValue="";this.div.style.display="none"},File:function(a){return this.Set(["LoadFile","Loading %1",a],null,null)},Log:function(){var b=[];for(var c=1,a=this.log.length;c<a;c++){b[c]=this.log[c].text}return b.join("\n")}};MathJax.Hub={config:{root:"",config:[],styleSheets:[],styles:{".MathJax_Preview":{color:"#888"}},jax:[],extensions:[],preJax:null,postJax:null,displayAlign:"center",displayIndent:"0",preRemoveClass:"MathJax_Preview",showProcessingMessages:true,messageStyle:"normal",delayStartupUntil:"none",skipStartupTypeset:false,elements:[],positionToHash:true,showMathMenu:true,showMathMenuMSIE:true,menuSettings:{zoom:"None",CTRL:false,ALT:false,CMD:false,Shift:false,discoverable:false,zscale:"200%",renderer:null,font:"Auto",context:"MathJax",locale:null,mpContext:false,mpMouse:false,texHints:true,FastPreview:null,assistiveMML:null,inTabOrder:true,semantics:false},errorSettings:{message:["[",["MathProcessingError","Math Processing Error"],"]"],style:{color:"#CC0000","font-style":"italic"}},ignoreMMLattributes:{}},preProcessors:MathJax.Callback.Hooks(true),inputJax:{},outputJax:{order:{}},processSectionDelay:50,processUpdateTime:250,processUpdateDelay:10,signal:MathJax.Callback.Signal("Hub"),Config:function(a){this.Insert(this.config,a);if(this.config.Augment){this.Augment(this.config.Augment)}},CombineConfig:function(c,f){var b=this.config,g,e;c=c.split(/\./);for(var d=0,a=c.length;d<a;d++){g=c[d];if(!b[g]){b[g]={}}e=b;b=b[g]}e[g]=b=this.Insert(f,b);return b},Register:{PreProcessor:function(){return MathJax.Hub.preProcessors.Add.apply(MathJax.Hub.preProcessors,arguments)},MessageHook:function(){return MathJax.Hub.signal.MessageHook.apply(MathJax.Hub.signal,arguments)},StartupHook:function(){return MathJax.Hub.Startup.signal.MessageHook.apply(MathJax.Hub.Startup.signal,arguments)},LoadHook:function(){return MathJax.Ajax.LoadHook.apply(MathJax.Ajax,arguments)}},UnRegister:{PreProcessor:function(a){MathJax.Hub.preProcessors.Remove(a)},MessageHook:function(a){MathJax.Hub.signal.RemoveHook(a)},StartupHook:function(a){MathJax.Hub.Startup.signal.RemoveHook(a)},LoadHook:function(a){MathJax.Ajax.removeHook(a)}},getAllJax:function(e){var c=[],b=this.elementScripts(e);for(var d=0,a=b.length;d<a;d++){if(b[d].MathJax&&b[d].MathJax.elementJax){c.push(b[d].MathJax.elementJax)}}return c},getJaxByType:function(f,e){var c=[],b=this.elementScripts(e);for(var d=0,a=b.length;d<a;d++){if(b[d].MathJax&&b[d].MathJax.elementJax&&b[d].MathJax.elementJax.mimeType===f){c.push(b[d].MathJax.elementJax)}}return c},getJaxByInputType:function(f,e){var c=[],b=this.elementScripts(e);for(var d=0,a=b.length;d<a;d++){if(b[d].MathJax&&b[d].MathJax.elementJax&&b[d].type&&b[d].type.replace(/ *;(.|\s)*/,"")===f){c.push(b[d].MathJax.elementJax)}}return c},getJaxFor:function(a){if(typeof(a)==="string"){a=document.getElementById(a)}if(a&&a.MathJax){return a.MathJax.elementJax}if(this.isMathJaxNode(a)){if(!a.isMathJax){a=a.firstChild}while(a&&!a.jaxID){a=a.parentNode}if(a){return MathJax.OutputJax[a.jaxID].getJaxFromMath(a)}}return null},isJax:function(a){if(typeof(a)==="string"){a=document.getElementById(a)}if(this.isMathJaxNode(a)){return 1}if(a&&(a.tagName||"").toLowerCase()==="script"){if(a.MathJax){return(a.MathJax.state===MathJax.ElementJax.STATE.PROCESSED?1:-1)}if(a.type&&this.inputJax[a.type.replace(/ *;(.|\s)*/,"")]){return -1}}return 0},isMathJaxNode:function(a){return !!a&&(a.isMathJax||(a.className||"")==="MathJax_MathML")},setRenderer:function(d,c){if(!d){return}if(!MathJax.OutputJax[d]){this.config.menuSettings.renderer="";var b="[MathJax]/jax/output/"+d+"/config.js";return MathJax.Ajax.Require(b,["setRenderer",this,d,c])}else{this.config.menuSettings.renderer=d;if(c==null){c="jax/mml"}var a=this.outputJax;if(a[c]&&a[c].length){if(d!==a[c][0].id){a[c].unshift(MathJax.OutputJax[d]);return this.signal.Post(["Renderer Selected",d])}}return null}},Queue:function(){return this.queue.Push.apply(this.queue,arguments)},Typeset:function(c,d){if(!MathJax.isReady){return null}var b=this.elementCallback(c,d);if(b.count){var a=MathJax.Callback.Queue(["PreProcess",this,b.elements],["Process",this,b.elements])}return a.Push(b.callback)},PreProcess:function(e,g){var c=this.elementCallback(e,g);var b=MathJax.Callback.Queue();if(c.count){var f=(c.count===1?[c.elements]:c.elements);b.Push(["Post",this.signal,["Begin PreProcess",c.elements]]);for(var d=0,a=f.length;d<a;d++){if(f[d]){b.Push(["Execute",this.preProcessors,f[d]])}}b.Push(["Post",this.signal,["End PreProcess",c.elements]])}return b.Push(c.callback)},Process:function(a,b){return this.takeAction("Process",a,b)},Update:function(a,b){return this.takeAction("Update",a,b)},Reprocess:function(a,b){return this.takeAction("Reprocess",a,b)},Rerender:function(a,b){return this.takeAction("Rerender",a,b)},takeAction:function(g,d,h){var c=this.elementCallback(d,h);var f=c.elements;var a=MathJax.Callback.Queue(["Clear",this.signal]);var e={scripts:[],start:new Date().getTime(),i:0,j:0,jax:{},jaxIDs:[]};if(c.count){var b=["Delay",MathJax.Callback,this.processSectionDelay];if(!b[2]){b={}}a.Push(["Post",this.signal,["Begin "+g,f]],["Post",this.signal,["Begin Math",f,g]],["prepareScripts",this,g,f,e],["Post",this.signal,["Begin Math Input",f,g]],["processInput",this,e],["Post",this.signal,["End Math Input",f,g]],b,["prepareOutput",this,e,"preProcess"],b,["Post",this.signal,["Begin Math Output",f,g]],["processOutput",this,e],["Post",this.signal,["End Math Output",f,g]],b,["prepareOutput",this,e,"postProcess"],b,["Post",this.signal,["End Math",f,g]],["Post",this.signal,["End "+g,f]])}return a.Push(c.callback)},scriptAction:{Process:function(a){},Update:function(b){var a=b.MathJax.elementJax;if(a&&a.needsUpdate()){a.Remove(true);b.MathJax.state=a.STATE.UPDATE}else{b.MathJax.state=a.STATE.PROCESSED}},Reprocess:function(b){var a=b.MathJax.elementJax;if(a){a.Remove(true);b.MathJax.state=a.STATE.UPDATE}},Rerender:function(b){var a=b.MathJax.elementJax;if(a){a.Remove(true);b.MathJax.state=a.STATE.OUTPUT}}},prepareScripts:function(h,e,g){if(arguments.callee.disabled){return}var b=this.elementScripts(e);var f=MathJax.ElementJax.STATE;for(var d=0,a=b.length;d<a;d++){var c=b[d];if(c.type&&this.inputJax[c.type.replace(/ *;(.|\n)*/,"")]){if(c.MathJax){if(c.MathJax.elementJax&&c.MathJax.elementJax.hover){MathJax.Extension.MathEvents.Hover.ClearHover(c.MathJax.elementJax)}if(c.MathJax.state!==f.PENDING){this.scriptAction[h](c)}}if(!c.MathJax){c.MathJax={state:f.PENDING}}if(c.MathJax.error){delete c.MathJax.error}if(c.MathJax.state!==f.PROCESSED){g.scripts.push(c)}}}},checkScriptSiblings:function(a){if(a.MathJax.checked){return}var b=this.config,f=a.previousSibling;if(f&&f.nodeName==="#text"){var d,e,c=a.nextSibling;if(c&&c.nodeName!=="#text"){c=null}if(b.preJax){if(typeof(b.preJax)==="string"){b.preJax=new RegExp(b.preJax+"$")}d=f.nodeValue.match(b.preJax)}if(b.postJax&&c){if(typeof(b.postJax)==="string"){b.postJax=new RegExp("^"+b.postJax)}e=c.nodeValue.match(b.postJax)}if(d&&(!b.postJax||e)){f.nodeValue=f.nodeValue.replace(b.preJax,(d.length>1?d[1]:""));f=null}if(e&&(!b.preJax||d)){c.nodeValue=c.nodeValue.replace(b.postJax,(e.length>1?e[1]:""))}if(f&&!f.nodeValue.match(/\S/)){f=f.previousSibling}}if(b.preRemoveClass&&f&&f.className===b.preRemoveClass){a.MathJax.preview=f}a.MathJax.checked=1},processInput:function(a){var b,i=MathJax.ElementJax.STATE;var h,e,d=a.scripts.length;try{while(a.i<d){h=a.scripts[a.i];if(!h){a.i++;continue}e=h.previousSibling;if(e&&e.className==="MathJax_Error"){e.parentNode.removeChild(e)}if(!h.MathJax||h.MathJax.state===i.PROCESSED){a.i++;continue}if(!h.MathJax.elementJax||h.MathJax.state===i.UPDATE){this.checkScriptSiblings(h);var g=h.type.replace(/ *;(.|\s)*/,"");var j=this.inputJax[g];b=j.Process(h,a);if(typeof b==="function"){if(b.called){continue}this.RestartAfter(b)}b=b.Attach(h,j.id);this.saveScript(b,a,h,i);this.postInputHooks.Execute(b,j.id,h)}else{if(h.MathJax.state===i.OUTPUT){this.saveScript(h.MathJax.elementJax,a,h,i)}}a.i++;var c=new Date().getTime();if(c-a.start>this.processUpdateTime&&a.i<a.scripts.length){a.start=c;this.RestartAfter(MathJax.Callback.Delay(1))}}}catch(f){return this.processError(f,a,"Input")}if(a.scripts.length&&this.config.showProcessingMessages){MathJax.Message.Set(["ProcessMath","Processing math: %1%%",100],0)}a.start=new Date().getTime();a.i=a.j=0;return null},postInputHooks:MathJax.Callback.Hooks(true),saveScript:function(a,d,b,c){if(!this.outputJax[a.mimeType]){b.MathJax.state=c.UPDATE;throw Error("No output jax registered for "+a.mimeType)}a.outputJax=this.outputJax[a.mimeType][0].id;if(!d.jax[a.outputJax]){if(d.jaxIDs.length===0){d.jax[a.outputJax]=d.scripts}else{if(d.jaxIDs.length===1){d.jax[d.jaxIDs[0]]=d.scripts.slice(0,d.i)}d.jax[a.outputJax]=[]}d.jaxIDs.push(a.outputJax)}if(d.jaxIDs.length>1){d.jax[a.outputJax].push(b)}b.MathJax.state=c.OUTPUT},prepareOutput:function(c,f){while(c.j<c.jaxIDs.length){var e=c.jaxIDs[c.j],d=MathJax.OutputJax[e];if(d[f]){try{var a=d[f](c);if(typeof a==="function"){if(a.called){continue}this.RestartAfter(a)}}catch(b){if(!b.restart){MathJax.Message.Set(["PrepError","Error preparing %1 output (%2)",e,f],null,600);MathJax.Hub.lastPrepError=b;c.j++}return MathJax.Callback.After(["prepareOutput",this,c,f],b.restart)}}c.j++}return null},processOutput:function(h){var b,g=MathJax.ElementJax.STATE,d,a=h.scripts.length;try{while(h.i<a){d=h.scripts[h.i];if(!d||!d.MathJax||d.MathJax.error){h.i++;continue}var c=d.MathJax.elementJax;if(!c){h.i++;continue}b=MathJax.OutputJax[c.outputJax].Process(d,h);if(b!==false){d.MathJax.state=g.PROCESSED;if(d.MathJax.preview){d.MathJax.preview.innerHTML=""}this.signal.Post(["New Math",c.inputID])}h.i++;var e=new Date().getTime();if(e-h.start>this.processUpdateTime&&h.i<h.scripts.length){h.start=e;this.RestartAfter(MathJax.Callback.Delay(this.processUpdateDelay))}}}catch(f){return this.processError(f,h,"Output")}if(h.scripts.length&&this.config.showProcessingMessages){MathJax.Message.Set(["TypesetMath","Typesetting math: %1%%",100],0);MathJax.Message.Clear(0)}h.i=h.j=0;return null},processMessage:function(d,b){var a=Math.floor(d.i/(d.scripts.length)*100);var c=(b==="Output"?["TypesetMath","Typesetting math: %1%%"]:["ProcessMath","Processing math: %1%%"]);if(this.config.showProcessingMessages){MathJax.Message.Set(c.concat(a),0)}},processError:function(b,c,a){if(!b.restart){if(!this.config.errorSettings.message){throw b}this.formatError(c.scripts[c.i],b);c.i++}this.processMessage(c,a);return MathJax.Callback.After(["process"+a,this,c],b.restart)},formatError:function(b,f){var h=function(l,k,j,i){return MathJax.Localization._(l,k,j,i)};var e=h("ErrorMessage","Error: %1",f.message)+"\n";if(f.sourceURL||f.fileName){e+="\n"+h("ErrorFile","file: %1",f.sourceURL||f.fileName)}if(f.line||f.lineNumber){e+="\n"+h("ErrorLine","line: %1",f.line||f.lineNumber)}e+="\n\n"+h("ErrorTips","Debugging tips: use %1, inspect %2 in the browser console","'unpacked/MathJax.js'","'MathJax.Hub.lastError'");b.MathJax.error=MathJax.OutputJax.Error.Jax(e,b);if(b.MathJax.elementJax){b.MathJax.error.inputID=b.MathJax.elementJax.inputID}var g=this.config.errorSettings;var a=h(g.messageId,g.message);var c=MathJax.HTML.Element("span",{className:"MathJax_Error",jaxID:"Error",isMathJax:true,id:b.MathJax.error.inputID+"-Frame"},[["span",null,a]]);MathJax.Ajax.Require("[MathJax]/extensions/MathEvents.js",function(){var j=MathJax.Extension.MathEvents.Event,i=MathJax.Hub;c.oncontextmenu=j.Menu;c.onmousedown=j.Mousedown;c.onkeydown=j.Keydown;c.tabIndex=i.getTabOrder(i.getJaxFor(b))});var d=document.getElementById(c.id);if(d){d.parentNode.removeChild(d)}b.parentNode.insertBefore(c,b);if(b.MathJax.preview){b.MathJax.preview.innerHTML=""}this.lastError=f;this.signal.Post(["Math Processing Error",b,f])},RestartAfter:function(a){throw this.Insert(Error("restart"),{restart:MathJax.Callback(a)})},elementCallback:function(c,f){if(f==null&&(c instanceof Array||typeof c==="function")){try{MathJax.Callback(c);f=c;c=null}catch(d){}}if(c==null){c=this.config.elements||[]}if(this.isHTMLCollection(c)){c=this.HTMLCollection2Array(c)}if(!(c instanceof Array)){c=[c]}c=[].concat(c);for(var b=0,a=c.length;b<a;b++){if(typeof(c[b])==="string"){c[b]=document.getElementById(c[b])}}if(!document.body){document.body=document.getElementsByTagName("body")[0]}if(c.length==0){c.push(document.body)}if(!f){f={}}return{count:c.length,elements:(c.length===1?c[0]:c),callback:f}},elementScripts:function(e){var b=[];if(e instanceof Array||this.isHTMLCollection(e)){for(var d=0,a=e.length;d<a;d++){var f=0;for(var c=0;c<d&&!f;c++){f=e[c].contains(e[d])}if(!f){b.push.apply(b,this.elementScripts(e[d]))}}return b}if(typeof(e)==="string"){e=document.getElementById(e)}if(!document.body){document.body=document.getElementsByTagName("body")[0]}if(e==null){e=document.body}if(e.tagName!=null&&e.tagName.toLowerCase()==="script"){return[e]}b=e.getElementsByTagName("script");if(this.msieHTMLCollectionBug){b=this.HTMLCollection2Array(b)}return b},isHTMLCollection:function(a){return("HTMLCollection" in window&&typeof(a)==="object"&&a instanceof HTMLCollection)},HTMLCollection2Array:function(c){if(!this.msieHTMLCollectionBug){return[].slice.call(c)}var b=[];for(var d=0,a=c.length;d<a;d++){b[d]=c[d]}return b},Insert:function(c,a){for(var b in a){if(a.hasOwnProperty(b)){if(typeof a[b]==="object"&&!(a[b] instanceof Array)&&(typeof c[b]==="object"||typeof c[b]==="function")){this.Insert(c[b],a[b])}else{c[b]=a[b]}}}return c},getTabOrder:function(a){return this.config.menuSettings.inTabOrder?0:-1},SplitList:("trim" in String.prototype?function(a){return a.trim().split(/\s+/)}:function(a){return a.replace(/^\s+/,"").replace(/\s+$/,"").split(/\s+/)})};MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles);MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style});MathJax.Extension={};MathJax.Hub.Configured=MathJax.Callback({});MathJax.Hub.Startup={script:"",queue:MathJax.Callback.Queue(),signal:MathJax.Callback.Signal("Startup"),params:{},Config:function(){this.queue.Push(["Post",this.signal,"Begin Config"]);if(this.params.locale){MathJax.Localization.resetLocale(this.params.locale);MathJax.Hub.config.menuSettings.locale=this.params.locale}if(this.params.config){var c=this.params.config.split(/,/);for(var b=0,a=c.length;b<a;b++){if(!c[b].match(/\.js$/)){c[b]+=".js"}this.queue.Push(["Require",MathJax.Ajax,this.URL("config",c[b])])}}this.queue.Push(["Config",MathJax.Hub,MathJax.AuthorConfig]);if(this.script.match(/\S/)){this.queue.Push(this.script+";\n1;")}this.queue.Push(["ConfigDelay",this],["ConfigBlocks",this],[function(d){return d.loadArray(MathJax.Hub.config.config,"config",null,true)},this],["Post",this.signal,"End Config"])},ConfigDelay:function(){var a=this.params.delayStartupUntil||MathJax.Hub.config.delayStartupUntil;if(a==="onload"){return this.onload}if(a==="configured"){return MathJax.Hub.Configured}return a},ConfigBlocks:function(){var c=document.getElementsByTagName("script");var f=null,b=MathJax.Callback.Queue();for(var d=0,a=c.length;d<a;d++){var e=String(c[d].type).replace(/ /g,"");if(e.match(/^text\/x-mathjax-config(;.*)?$/)&&!e.match(/;executed=true/)){c[d].type+=";executed=true";f=b.Push(c[d].innerHTML+";\n1;")}}return f},Cookie:function(){return this.queue.Push(["Post",this.signal,"Begin Cookie"],["Get",MathJax.HTML.Cookie,"menu",MathJax.Hub.config.menuSettings],[function(e){var d=e.menuSettings;if(d.locale){MathJax.Localization.resetLocale(d.locale)}var g=e.menuSettings.renderer,b=e.jax;if(g){var c="output/"+g;b.sort();for(var f=0,a=b.length;f<a;f++){if(b[f].substr(0,7)==="output/"){break}}if(f==a-1){b.pop()}else{while(f<a){if(b[f]===c){b.splice(f,1);break}f++}}b.unshift(c)}if(d.CHTMLpreview!=null){if(d.FastPreview==null){d.FastPreview=d.CHTMLpreview}delete d.CHTMLpreview}if(d.FastPreview&&!MathJax.Extension["fast-preview"]){MathJax.Hub.config.extensions.push("fast-preview.js")}if(e.menuSettings.assistiveMML&&!MathJax.Extension.AssistiveMML){MathJax.Hub.config.extensions.push("AssistiveMML.js")}},MathJax.Hub.config],["Post",this.signal,"End Cookie"])},Styles:function(){return this.queue.Push(["Post",this.signal,"Begin Styles"],["loadArray",this,MathJax.Hub.config.styleSheets,"config"],["Styles",MathJax.Ajax,MathJax.Hub.config.styles],["Post",this.signal,"End Styles"])},Jax:function(){var f=MathJax.Hub.config,c=MathJax.Hub.outputJax;for(var g=0,b=f.jax.length,d=0;g<b;g++){var e=f.jax[g].substr(7);if(f.jax[g].substr(0,7)==="output/"&&c.order[e]==null){c.order[e]=d;d++}}var a=MathJax.Callback.Queue();return a.Push(["Post",this.signal,"Begin Jax"],["loadArray",this,f.jax,"jax","config.js"],["Post",this.signal,"End Jax"])},Extensions:function(){var a=MathJax.Callback.Queue();return a.Push(["Post",this.signal,"Begin Extensions"],["loadArray",this,MathJax.Hub.config.extensions,"extensions"],["Post",this.signal,"End Extensions"])},Message:function(){MathJax.Message.Init(true)},Menu:function(){var b=MathJax.Hub.config.menuSettings,a=MathJax.Hub.outputJax,d;for(var c in a){if(a.hasOwnProperty(c)){if(a[c].length){d=a[c];break}}}if(d&&d.length){if(b.renderer&&b.renderer!==d[0].id){d.unshift(MathJax.OutputJax[b.renderer])}b.renderer=d[0].id}},Hash:function(){if(MathJax.Hub.config.positionToHash&&document.location.hash&&document.body&&document.body.scrollIntoView){var d=document.location.hash.substr(1);var f=document.getElementById(d);if(!f){var c=document.getElementsByTagName("a");for(var e=0,b=c.length;e<b;e++){if(c[e].name===d){f=c[e];break}}}if(f){while(!f.scrollIntoView){f=f.parentNode}f=this.HashCheck(f);if(f&&f.scrollIntoView){setTimeout(function(){f.scrollIntoView(true)},1)}}}},HashCheck:function(b){var a=MathJax.Hub.getJaxFor(b);if(a&&MathJax.OutputJax[a.outputJax].hashCheck){b=MathJax.OutputJax[a.outputJax].hashCheck(b)}return b},MenuZoom:function(){if(MathJax.Hub.config.showMathMenu){if(!MathJax.Extension.MathMenu){setTimeout(function(){MathJax.Callback.Queue(["Require",MathJax.Ajax,"[MathJax]/extensions/MathMenu.js",{}],["loadDomain",MathJax.Localization,"MathMenu"])},1000)}else{setTimeout(MathJax.Callback(["loadDomain",MathJax.Localization,"MathMenu"]),1000)}if(!MathJax.Extension.MathZoom){setTimeout(MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathZoom.js",{}]),2000)}}},onLoad:function(){var a=this.onload=MathJax.Callback(function(){MathJax.Hub.Startup.signal.Post("onLoad")});if(document.body&&document.readyState){if(MathJax.Hub.Browser.isMSIE){if(document.readyState==="complete"){return[a]}}else{if(document.readyState!=="loading"){return[a]}}}if(window.addEventListener){window.addEventListener("load",a,false);if(!this.params.noDOMContentEvent){window.addEventListener("DOMContentLoaded",a,false)}}else{if(window.attachEvent){window.attachEvent("onload",a)}else{window.onload=a}}return a},Typeset:function(a,b){if(MathJax.Hub.config.skipStartupTypeset){return function(){}}return this.queue.Push(["Post",this.signal,"Begin Typeset"],["Typeset",MathJax.Hub,a,b],["Post",this.signal,"End Typeset"])},URL:function(b,a){if(!a.match(/^([a-z]+:\/\/|\[|\/)/)){a="[MathJax]/"+b+"/"+a}return a},loadArray:function(b,f,c,a){if(b){if(!(b instanceof Array)){b=[b]}if(b.length){var h=MathJax.Callback.Queue(),j={},e;for(var g=0,d=b.length;g<d;g++){e=this.URL(f,b[g]);if(c){e+="/"+c}if(a){h.Push(["Require",MathJax.Ajax,e,j])}else{h.Push(MathJax.Ajax.Require(e,j))}}return h.Push({})}}return null}};(function(d){var b=window[d],e="["+d+"]";var c=b.Hub,a=b.Ajax,f=b.Callback;var g=MathJax.Object.Subclass({JAXFILE:"jax.js",require:null,config:{},Init:function(i,h){if(arguments.length===0){return this}return(this.constructor.Subclass(i,h))()},Augment:function(k,j){var i=this.constructor,h={};if(k!=null){for(var l in k){if(k.hasOwnProperty(l)){if(typeof k[l]==="function"){i.protoFunction(l,k[l])}else{h[l]=k[l]}}}if(k.toString!==i.prototype.toString&&k.toString!=={}.toString){i.protoFunction("toString",k.toString)}}c.Insert(i.prototype,h);i.Augment(null,j);return this},Translate:function(h,i){throw Error(this.directory+"/"+this.JAXFILE+" failed to define the Translate() method")},Register:function(h){},Config:function(){this.config=c.CombineConfig(this.id,this.config);if(this.config.Augment){this.Augment(this.config.Augment)}},Startup:function(){},loadComplete:function(i){if(i==="config.js"){return a.loadComplete(this.directory+"/"+i)}else{var h=f.Queue();h.Push(c.Register.StartupHook("End Config",{}),["Post",c.Startup.signal,this.id+" Jax Config"],["Config",this],["Post",c.Startup.signal,this.id+" Jax Require"],[function(j){return MathJax.Hub.Startup.loadArray(j.require,this.directory)},this],[function(j,k){return MathJax.Hub.Startup.loadArray(j.extensions,"extensions/"+k)},this.config||{},this.id],["Post",c.Startup.signal,this.id+" Jax Startup"],["Startup",this],["Post",c.Startup.signal,this.id+" Jax Ready"]);if(this.copyTranslate){h.Push([function(j){j.preProcess=j.preTranslate;j.Process=j.Translate;j.postProcess=j.postTranslate},this.constructor.prototype])}return h.Push(["loadComplete",a,this.directory+"/"+i])}}},{id:"Jax",version:"2.6.0",directory:e+"/jax",extensionDir:e+"/extensions"});b.InputJax=g.Subclass({elementJax:"mml",sourceMenuTitle:["Original","Original Form"],copyTranslate:true,Process:function(l,q){var j=f.Queue(),o;var k=this.elementJax;if(!(k instanceof Array)){k=[k]}for(var n=0,h=k.length;n<h;n++){o=b.ElementJax.directory+"/"+k[n]+"/"+this.JAXFILE;if(!this.require){this.require=[]}else{if(!(this.require instanceof Array)){this.require=[this.require]}}this.require.push(o);j.Push(a.Require(o))}o=this.directory+"/"+this.JAXFILE;var p=j.Push(a.Require(o));if(!p.called){this.constructor.prototype.Process=function(){if(!p.called){return p}throw Error(o+" failed to load properly")}}k=c.outputJax["jax/"+k[0]];if(k){j.Push(a.Require(k[0].directory+"/"+this.JAXFILE))}return j.Push({})},needsUpdate:function(h){var i=h.SourceElement();return(h.originalText!==b.HTML.getScript(i))},Register:function(h){if(!c.inputJax){c.inputJax={}}c.inputJax[h]=this}},{id:"InputJax",version:"2.6.0",directory:g.directory+"/input",extensionDir:g.extensionDir});b.OutputJax=g.Subclass({copyTranslate:true,preProcess:function(j){var i,h=this.directory+"/"+this.JAXFILE;this.constructor.prototype.preProcess=function(k){if(!i.called){return i}throw Error(h+" failed to load properly")};i=a.Require(h);return i},Register:function(i){var h=c.outputJax;if(!h[i]){h[i]=[]}if(h[i].length&&(this.id===c.config.menuSettings.renderer||(h.order[this.id]||0)<(h.order[h[i][0].id]||0))){h[i].unshift(this)}else{h[i].push(this)}if(!this.require){this.require=[]}else{if(!(this.require instanceof Array)){this.require=[this.require]}}this.require.push(b.ElementJax.directory+"/"+(i.split(/\//)[1])+"/"+this.JAXFILE)},Remove:function(h){}},{id:"OutputJax",version:"2.6.0",directory:g.directory+"/output",extensionDir:g.extensionDir,fontDir:e+(b.isPacked?"":"/..")+"/fonts",imageDir:e+(b.isPacked?"":"/..")+"/images"});b.ElementJax=g.Subclass({Init:function(i,h){return this.constructor.Subclass(i,h)},inputJax:null,outputJax:null,inputID:null,originalText:"",mimeType:"",sourceMenuTitle:["MathMLcode","MathML Code"],Text:function(i,j){var h=this.SourceElement();b.HTML.setScript(h,i);h.MathJax.state=this.STATE.UPDATE;return c.Update(h,j)},Reprocess:function(i){var h=this.SourceElement();h.MathJax.state=this.STATE.UPDATE;return c.Reprocess(h,i)},Update:function(h){return this.Rerender(h)},Rerender:function(i){var h=this.SourceElement();h.MathJax.state=this.STATE.OUTPUT;return c.Process(h,i)},Remove:function(h){if(this.hover){this.hover.clear(this)}b.OutputJax[this.outputJax].Remove(this);if(!h){c.signal.Post(["Remove Math",this.inputID]);this.Detach()}},needsUpdate:function(){return b.InputJax[this.inputJax].needsUpdate(this)},SourceElement:function(){return document.getElementById(this.inputID)},Attach:function(i,j){var h=i.MathJax.elementJax;if(i.MathJax.state===this.STATE.UPDATE){h.Clone(this)}else{h=i.MathJax.elementJax=this;if(i.id){this.inputID=i.id}else{i.id=this.inputID=b.ElementJax.GetID();this.newID=1}}h.originalText=b.HTML.getScript(i);h.inputJax=j;if(h.root){h.root.inputID=h.inputID}return h},Detach:function(){var h=this.SourceElement();if(!h){return}try{delete h.MathJax}catch(i){h.MathJax=null}if(this.newID){h.id=""}},Clone:function(h){var i;for(i in this){if(!this.hasOwnProperty(i)){continue}if(typeof(h[i])==="undefined"&&i!=="newID"){delete this[i]}}for(i in h){if(!h.hasOwnProperty(i)){continue}if(typeof(this[i])==="undefined"||(this[i]!==h[i]&&i!=="inputID")){this[i]=h[i]}}}},{id:"ElementJax",version:"2.6.0",directory:g.directory+"/element",extensionDir:g.extensionDir,ID:0,STATE:{PENDING:1,PROCESSED:2,UPDATE:3,OUTPUT:4},GetID:function(){this.ID++;return"MathJax-Element-"+this.ID},Subclass:function(){var h=g.Subclass.apply(this,arguments);h.loadComplete=this.prototype.loadComplete;return h}});b.ElementJax.prototype.STATE=b.ElementJax.STATE;b.OutputJax.Error={id:"Error",version:"2.6.0",config:{},errors:0,ContextMenu:function(){return b.Extension.MathEvents.Event.ContextMenu.apply(b.Extension.MathEvents.Event,arguments)},Mousedown:function(){return b.Extension.MathEvents.Event.AltContextMenu.apply(b.Extension.MathEvents.Event,arguments)},getJaxFromMath:function(h){return(h.nextSibling.MathJax||{}).error},Jax:function(j,i){var h=MathJax.Hub.inputJax[i.type.replace(/ *;(.|\s)*/,"")];this.errors++;return{inputJax:(h||{id:"Error"}).id,outputJax:"Error",inputID:"MathJax-Error-"+this.errors,sourceMenuTitle:["ErrorMessage","Error Message"],sourceMenuFormat:"Error",originalText:MathJax.HTML.getScript(i),errorText:j}}};b.InputJax.Error={id:"Error",version:"2.6.0",config:{},sourceMenuTitle:["Original","Original Form"]}})("MathJax");(function(o){var h=window[o];if(!h){h=window[o]={}}var d=h.Hub;var s=d.Startup;var w=d.config;var g=document.head||(document.getElementsByTagName("head")[0]);if(!g){g=document.childNodes[0]}var b=(document.documentElement||document).getElementsByTagName("script");if(b.length===0&&g.namespaceURI){b=document.getElementsByTagNameNS(g.namespaceURI,"script")}var f=new RegExp("(^|/)"+o+"\\.js(\\?.*)?$");for(var q=b.length-1;q>=0;q--){if((b[q].src||"").match(f)){s.script=b[q].innerHTML;if(RegExp.$2){var t=RegExp.$2.substr(1).split(/\&/);for(var p=0,l=t.length;p<l;p++){var n=t[p].match(/(.*)=(.*)/);if(n){s.params[unescape(n[1])]=unescape(n[2])}}}w.root=b[q].src.replace(/(^|\/)[^\/]*(\?.*)?$/,"").replace(/^(https?:\/\/cdn.mathjax.org\/mathjax\/)(latest)/,"$1"+h.version.split(/\./).slice(0,2).join(".")+"-$2");h.Ajax.config.root=w.root;break}}var k=navigator.userAgent;var a={isMac:(navigator.platform.substr(0,3)==="Mac"),isPC:(navigator.platform.substr(0,3)==="Win"),isMSIE:("ActiveXObject" in window&&"clipboardData" in window),isEdge:("MSGestureEvent" in window&&"chrome" in window&&window.chrome.loadTimes==null),isFirefox:(!!k.match(/Gecko\//)&&!k.match(/like Gecko/)),isSafari:(!!k.match(/ (Apple)?WebKit\//)&&!k.match(/ like iPhone /)&&(!window.chrome||window.chrome.app==null)),isChrome:("chrome" in window&&window.chrome.loadTimes!=null),isOpera:("opera" in window&&window.opera.version!=null),isKonqueror:("konqueror" in window&&navigator.vendor=="KDE"),versionAtLeast:function(y){var x=(this.version).split(".");y=(new String(y)).split(".");for(var z=0,j=y.length;z<j;z++){if(x[z]!=y[z]){return parseInt(x[z]||"0")>=parseInt(y[z])}}return true},Select:function(j){var i=j[d.Browser];if(i){return i(d.Browser)}return null}};var e=k.replace(/^Mozilla\/(\d+\.)+\d+ /,"").replace(/[a-z][-a-z0-9._: ]+\/\d+[^ ]*-[^ ]*\.([a-z][a-z])?\d+ /i,"").replace(/Gentoo |Ubuntu\/(\d+\.)*\d+ (\([^)]*\) )?/,"");d.Browser=d.Insert(d.Insert(new String("Unknown"),{version:"0.0"}),a);for(var v in a){if(a.hasOwnProperty(v)){if(a[v]&&v.substr(0,2)==="is"){v=v.slice(2);if(v==="Mac"||v==="PC"){continue}d.Browser=d.Insert(new String(v),a);var r=new RegExp(".*(Version/| Trident/.*; rv:)((?:\\d+\\.)+\\d+)|.*("+v+")"+(v=="MSIE"?" ":"/")+"((?:\\d+\\.)*\\d+)|(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)");var u=r.exec(e)||["","","","unknown","0.0"];d.Browser.name=(u[1]!=""?v:(u[3]||u[5]));d.Browser.version=u[2]||u[4]||u[6];break}}}try{d.Browser.Select({Safari:function(j){var i=parseInt((String(j.version).split("."))[0]);if(i>85){j.webkit=j.version}if(i>=538){j.version="8.0"}else{if(i>=537){j.version="7.0"}else{if(i>=536){j.version="6.0"}else{if(i>=534){j.version="5.1"}else{if(i>=533){j.version="5.0"}else{if(i>=526){j.version="4.0"}else{if(i>=525){j.version="3.1"}else{if(i>500){j.version="3.0"}else{if(i>400){j.version="2.0"}else{if(i>85){j.version="1.0"}}}}}}}}}}j.webkit=(navigator.appVersion.match(/WebKit\/(\d+)\./))[1];j.isMobile=(navigator.appVersion.match(/Mobile/i)!=null);j.noContextMenu=j.isMobile},Firefox:function(j){if((j.version==="0.0"||k.match(/Firefox/)==null)&&navigator.product==="Gecko"){var m=k.match(/[\/ ]rv:(\d+\.\d.*?)[\) ]/);if(m){j.version=m[1]}else{var i=(navigator.buildID||navigator.productSub||"0").substr(0,8);if(i>="20111220"){j.version="9.0"}else{if(i>="20111120"){j.version="8.0"}else{if(i>="20110927"){j.version="7.0"}else{if(i>="20110816"){j.version="6.0"}else{if(i>="20110621"){j.version="5.0"}else{if(i>="20110320"){j.version="4.0"}else{if(i>="20100121"){j.version="3.6"}else{if(i>="20090630"){j.version="3.5"}else{if(i>="20080617"){j.version="3.0"}else{if(i>="20061024"){j.version="2.0"}}}}}}}}}}}}j.isMobile=(navigator.appVersion.match(/Android/i)!=null||k.match(/ Fennec\//)!=null||k.match(/Mobile/)!=null)},Chrome:function(i){i.noContextMenu=i.isMobile=!!navigator.userAgent.match(/ Mobile[ \/]/)},Opera:function(i){i.version=opera.version()},Edge:function(i){i.isMobile=!!navigator.userAgent.match(/ Phone/)},MSIE:function(j){j.isMobile=!!navigator.userAgent.match(/ Phone/);j.isIE9=!!(document.documentMode&&(window.performance||window.msPerformance));MathJax.HTML.setScriptBug=!j.isIE9||document.documentMode<9;MathJax.Hub.msieHTMLCollectionBug=(document.documentMode<9);if(document.documentMode<10&&!s.params.NoMathPlayer){try{new ActiveXObject("MathPlayer.Factory.1");j.hasMathPlayer=true}catch(m){}try{if(j.hasMathPlayer){var i=document.createElement("object");i.id="mathplayer";i.classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987";g.appendChild(i);document.namespaces.add("m","http://www.w3.org/1998/Math/MathML");j.mpNamespace=true;if(document.readyState&&(document.readyState==="loading"||document.readyState==="interactive")){document.write('<?import namespace="m" implementation="#MathPlayer">');j.mpImported=true}}else{document.namespaces.add("mjx_IE_fix","http://www.w3.org/1999/xlink")}}catch(m){}}}})}catch(c){console.error(c.message)}d.Browser.Select(MathJax.Message.browsers);if(h.AuthorConfig&&typeof h.AuthorConfig.AuthorInit==="function"){h.AuthorConfig.AuthorInit()}d.queue=h.Callback.Queue();d.queue.Push(["Post",s.signal,"Begin"],["Config",s],["Cookie",s],["Styles",s],["Message",s],function(){var i=h.Callback.Queue(s.Jax(),s.Extensions());return i.Push({})},["Menu",s],s.onLoad(),function(){MathJax.isReady=true},["Typeset",s],["Hash",s],["MenuZoom",s],["Post",s.signal,"End"])})("MathJax")}}; diff --git a/pub/static/style.css b/pub/static/style.css @@ -0,0 +1,295 @@ +body { + color: #111; + background-color: #eee; + margin: 0px; + padding: 0px; + background-image: url('img/header_repeat2.png'); + background-repeat: repeat-x; + display: flex; + justify-content: space-around; +} + +nav { + width: 250px; + padding: 10px; +} + +main { + max-width: 1000px; + padding: 10px; +} + +img.translation { + float: left; + margin: 0px; + padding: 0px; + margin-right: 10px; + margin-top: -5px; +} + +#content { + /*width: 850px;*/ + float: left; +} + +#disqus_thread { + width: 850px; + float: right; + margin-top: 30px; + display: none; +} + +warning { + display:block; + margin:20px; + padding:15px; + background-color: #FFD2D2; + color: #444; + border-radius: 5px; + border: 2px solid #E0B3B3; +} + +note { + display:block; + margin:20px; + padding:15px; + background-color: #D8F5D8; + color: #444; + border-radius: 5px; + border: 2px solid #AFDFAF; +} + +author { + display:block; + margin:10px; + margin-top: 15px; + padding:10px; + background-color: #eee; + color: #444; + border-radius: 2px; + border: 2px solid #ccc; +} + +function { + cursor: pointer; + border-bottom: 1px solid #c1c1c1; +} + +def { + color: green; +} + +fun { + font-family: "Courier New", Courier, monospace; + color: #822; +} + +var { + font-style: normal; + font-family: "Courier New", Courier, monospace; + color: #227; +} + +p code { + +} + +pre code { + color: rgb(214, 210, 205); + background-color: rgb(32, 35, 36); + background-image: none; + border-color: rgb(67, 73, 76); + display: block; + background: #282B2E; + padding: 5px; + padding-left: 15px; + border-radius: 7px; + border: 3px solid #bbb; +} + +indepth strong.indepth_header { + display: block; + width: 100%; + text-align: center; + cursor: pointer; + color: #333; +} + +indepth { + display:block; + margin:20px; + padding: 10px; + background-color: #BDF; + color: #444; + border-radius: 5px; + border: 3px dotted #9BD; +} + +indepth p { + padding: 15px; + display: none; +} + +indepth img { + display: none; +} + +img { + display: block; + margin-left: auto; + margin-right: auto; + border-radius: 7px; + border: 3px solid #bbb; +} + +img.clean { + border: 0px; + border-radius: 0px; +} + +img.no_radius { + border-radius: 0px; +} + +img.left { + float: left; + margin: 10px; + border: 2px solid #bbb; + border-radius: 3px; + margin-right: 20px; +} + +img.right { + float: right; + margin: 10px; + margin-left: 20px; +} + +img.small { + width: 150px; + height: auto; +} + +img.medium { + width: 300px; + height: auto; +} + +img.large { + width: 500px; + height: auto; +} + +img.book { + margin-left: 100px; + margin-right: 50px; + height: 200px; + width: auto; + #margin-top: -5px; +} + +video { + position:relative; + display: block; + margin: 0px; + padding: 0px; +} + +video.clean { + border: 0px; +} + +.video { + position:relative; + cursor: pointer; + background-image: url('/img/start_video.png'); + width: 600px; + height: 450px; + margin: 0px; + padding: 0px; + margin-left: auto; + margin-right: auto; + border-radius: 3px; + border: 3px solid #bbb; +} + +.paused video { + position:relative; + z-index: -1; +} + + +audio { + display: block; + margin-left: auto; + margin-right: auto; +} + +/* == TABLES == */ +table, td { + border-bottom: 1px solid #AAA; + border-top: 1px solid #AAA; +} + +th { + border-bottom: 2px solid #888; +} + +table { + border-collapse: collapse; + text-align: center; + margin-left: auto; + margin-right: auto; +} + +th, td { + padding: 6px; +} + +tr:nth-child(even) { + background-color: #DDD; +} + +pre code function { + text-decoration: none; + border-bottom: 1px solid #343434; +} + +#hover { + display: none; + position: absolute; + color: #222; + width: 600px; + border-radius: 5px; + border: 5px dotted #ccc; + background-color: #eee; + background-image: url('img/header_repeat2.png'); + background-repeat: repeat-x; + opacity:0.95; + filter:alpha(opacity=95); /* For IE8 and earlier */ + padding: 10px; + padding-top: 0px; + padding-bottom: 0px; + font-size: 14px; +} +/* Elements */ +h1 { + color: #303236; + margin-bottom: -10px; +} + +h2 { + /*color: #394666;*/ + color: #37425d; + font-size: 22px; + margin-bottom: -10px; +} + +h3 { + color: #633739; + margin-bottom: -10px; +} + +a { + color: #58A; +} diff --git a/pub/video/getting-started/camera_circle.mp4 b/pub/video/getting-started/camera_circle.mp4 Binary files differ. diff --git a/pub/video/getting-started/camera_mouse.mp4 b/pub/video/getting-started/camera_mouse.mp4 Binary files differ. diff --git a/pub/video/getting-started/camera_smooth.mp4 b/pub/video/getting-started/camera_smooth.mp4 Binary files differ. diff --git a/pub/video/getting-started/coordinate_system_depth.mp4 b/pub/video/getting-started/coordinate_system_depth.mp4 Binary files differ. diff --git a/pub/video/getting-started/coordinate_system_no_depth.mp4 b/pub/video/getting-started/coordinate_system_no_depth.mp4 Binary files differ. diff --git a/pub/video/getting-started/shaders.mp4 b/pub/video/getting-started/shaders.mp4 Binary files differ. diff --git a/pub/video/getting-started/transformations.mp4 b/pub/video/getting-started/transformations.mp4 Binary files differ. diff --git a/templates/base.html b/templates/base.html @@ -0,0 +1,321 @@ +<!DOCTYPE html> +<html lang="ja"> +<head> + <meta charset="utf-8"/> + <title>LearnOpenGL</title> + <link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> + <link rel="stylesheet" href="../static/style.css" /> + <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script> + <script src="/static/functions.js"></script> +</head> +<body> + <nav> +<ol> + <li id="Introduction"> + <a href="https://learnopengl.com/Introduction">はじめに</a> + </li> + <li id="Getting-started"> + <span class="closed">入門</span> + <ol> + <li id="Getting-started/OpenGL"> + <a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a> + </li> + <li id="Getting-started/Creating-a-window"> + <a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a> + </li> + <li id="Getting-started/Hello-Window"> + <a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a> + </li> + <li id="Getting-started/Hello-Triangle"> + <a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a> + </li> + <li id="Getting-started/Shaders"> + <a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a> + </li> + <li id="Getting-started/Textures"> + <a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a> + </li> + <li id="Getting-started/Transformations"> + <a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a> + </li> + <li id="Getting-started/Coordinate-Systems"> + <a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a> + </li> + <li id="Getting-started/Camera"> + <a href="https://learnopengl.com/Getting-started/Camera">カメラ</a> + </li> + <li id="Getting-started/Review"> + <a href="https://learnopengl.com/Getting-started/Review">まとめ</a> + </li> + </ol> + </li> + <li id="Lighting"> + <span class="closed">Lighting </span> + <ol> + <li id="Lighting/Colors"> + <a href="https://learnopengl.com/Lighting/Colors">Colors </a> + </li> + <li id="Lighting/Basic-Lighting"> + <a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a> + </li> + <li id="Lighting/Materials"> + <a href="https://learnopengl.com/Lighting/Materials">Materials </a> + </li> + <li id="Lighting/Lighting-maps"> + <a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a> + </li> + <li id="Lighting/Light-casters"> + <a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a> + </li> + <li id="Lighting/Multiple-lights"> + <a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a> + </li> + <li id="Lighting/Review"> + <a href="https://learnopengl.com/Lighting/Review">Review </a> + </li> + </ol> + </li> + <li id="Model-Loading"> + <span class="closed">Model Loading </span> + <ol> + <li id="Model-Loading/Assimp"> + <a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a> + </li> + <li id="Model-Loading/Mesh"> + <a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a> + </li> + <li id="Model-Loading/Model"> + <a href="https://learnopengl.com/Model-Loading/Model">Model </a> + </li> + </ol> + </li> + <li id="Advanced-OpenGL"> + <span class="closed">Advanced OpenGL </span> + <ol> + <li id="Advanced-OpenGL/Depth-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a> + </li> + <li id="Advanced-OpenGL/Stencil-testing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a> + </li> + <li id="Advanced-OpenGL/Blending"> + <a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a> + </li> + <li id="Advanced-OpenGL/Face-culling"> + <a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a> + </li> + <li id="Advanced-OpenGL/Framebuffers"> + <a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a> + </li> + <li id="Advanced-OpenGL/Cubemaps"> + <a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a> + </li> + <li id="Advanced-OpenGL/Advanced-Data"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a> + </li> + <li id="Advanced-OpenGL/Advanced-GLSL"> + <a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a> + </li> + <li id="Advanced-OpenGL/Geometry-Shader"> + <a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a> + </li> + <li id="Advanced-OpenGL/Instancing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a> + </li> + <li id="Advanced-OpenGL/Anti-Aliasing"> + <a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting"> + <span class="closed">Advanced Lighting </span> + <ol> + <li id="Advanced-Lighting/Advanced-Lighting"> + <a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a> + </li> + <li id="Advanced-Lighting/Gamma-Correction"> + <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a> + </li> + <li id="Advanced-Lighting/Shadows"> + <span class="closed">Shadows </span> + <ol> + <li id="Advanced-Lighting/Shadows/Shadow-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a> + </li> + <li id="Advanced-Lighting/Shadows/Point-Shadows"> + <a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a> + </li> + </ol> + </li> + <li id="Advanced-Lighting/Normal-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a> + </li> + <li id="Advanced-Lighting/Parallax-Mapping"> + <a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a> + </li> + <li id="Advanced-Lighting/HDR"> + <a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a> + </li> + <li id="Advanced-Lighting/Bloom"> + <a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a> + </li> + <li id="Advanced-Lighting/Deferred-Shading"> + <a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a> + </li> + <li id="Advanced-Lighting/SSAO"> + <a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a> + </li> + </ol> + </li> + <li id="PBR"> + <span class="closed">PBR </span> + <ol> + <li id="PBR/Theory"> + <a href="https://learnopengl.com/PBR/Theory">Theory </a> + </li> + <li id="PBR/Lighting"> + <a href="https://learnopengl.com/PBR/Lighting">Lighting </a> + </li> + <li id="PBR/IBL"> + <span class="closed">IBL </span> + <ol> + <li id="PBR/IBL/Diffuse-irradiance"> + <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a> + </li> + <li id="PBR/IBL/Specular-IBL"> + <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="In-Practice"> + <span class="closed">In Practice </span> + <ol> + <li id="In-Practice/Debugging"> + <a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a> + </li> + <li id="In-Practice/Text-Rendering"> + <a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a> + </li> + <li id="In-Practice/2D-Game"> + <span class="closed">2D Game </span> + <ol> + <li id="In-Practice/2D-Game/Breakout"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a> + </li> + <li id="In-Practice/2D-Game/Setting-up"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a> + </li> + <li id="In-Practice/2D-Game/Rendering-Sprites"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a> + </li> + <li id="In-Practice/2D-Game/Levels"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a> + </li> + <li id="In-Practice/2D-Game/Collisions"> + <span class="closed">Collisions </span> + <ol> + <li id="In-Practice/2D-Game/Collisions/Ball"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-detection"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a> + </li> + <li id="In-Practice/2D-Game/Collisions/Collision-resolution"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a> + </li> + </ol> + </li> + <li id="In-Practice/2D-Game/Particles"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a> + </li> + <li id="In-Practice/2D-Game/Postprocessing"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a> + </li> + <li id="In-Practice/2D-Game/Powerups"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a> + </li> + <li id="In-Practice/2D-Game/Audio"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a> + </li> + <li id="In-Practice/2D-Game/Render-text"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a> + </li> + <li id="In-Practice/2D-Game/Final-thoughts"> + <a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a> + </li> + </ol> + </li> + </ol> + </li> + <li id="Guest-Articles"> + <span class="closed">Guest Articles </span> + <ol> + <li id="Guest-Articles/How-to-publish"> + <a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a> + </li> + <li id="Guest-Articles/2020"> + <span class="closed">2020 </span> + <ol> + <li id="Guest-Articles/2020/OIT"> + <span class="closed">OIT </span> + <ol> + <li id="Guest-Articles/2020/OIT/Introduction"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a> + </li> + <li id="Guest-Articles/2020/OIT/Weighted-Blended"> + <a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2020/Skeletal-Animation"> + <a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021"> + <span class="closed">2021 </span> + <ol> + <li id="Guest-Articles/2021/CSM"> + <a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a> + </li> + <li id="Guest-Articles/2021/Scene"> + <span class="closed">Scene </span> + <ol> + <li id="Guest-Articles/2021/Scene/Scene-Graph"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a> + </li> + <li id="Guest-Articles/2021/Scene/Frustum-Culling"> + <a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a> + </li> + </ol> + </li> + <li id="Guest-Articles/2021/Tessellation"> + <span class="closed">Tessellation </span> + <ol> + <li id="Guest-Articles/2021/Tessellation/Height-map"> + <a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a> + </li> + </ol> + </li> + </ol> + </li> + </ol> + </li> + <li id="Code-repository"> + <a href="https://learnopengl.com/Code-repository">Code repository </a> + </li> + <li id="Translations"> + <a href="https://learnopengl.com/Translations">Translations </a> + </li> + <li id="About"> + <a href="https://learnopengl.com/About">About </a> + </li> +</ol> + </nav> + <main> + <!--content--> + </main> +</body> +</html>