[
  {
    "path": ".gitignore",
    "content": "images/**\nblends/**\n*.kra~\n*.png~\n*main.o*\n*3d-game-shaders-for-beginners.o*\n*3d-game-shaders-for-beginners*\n!blends/*\n!blends/mill-scene/mill-scene-low-poly-backup.blend\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n<img src=\"https://i.imgur.com/x8rtGr4.gif\" alt=\"3D Game Shaders For Beginners\" title=\"3D Game Shaders For Beginners\">\n</p>\n\n# 3D Game Shaders For Beginners\n\nInterested in adding\ntextures,\nlighting,\nshadows,\nnormal maps,\nglowing objects,\nambient occlusion,\nreflections,\nrefractions,\nand more to your 3D game?\nGreat!\nBelow is a collection of shading techniques that will take your game visuals to new heights.\nI've explained each technique in such a way that you can take what you learn here and apply/port it to\nwhatever stack you use—be it Godot, Unity, Unreal, or something else.\nFor the glue in between the shaders,\nI've chosen the fabulous Panda3D game engine and the OpenGL Shading Language (GLSL).\nSo if that is your stack, then you'll also get the benefit of learning how to use these\nshading techniques with Panda3D and OpenGL specifically.\n\n## Table Of Contents\n\n- [Setup](sections/setup.md)\n- [Building The Demo](sections/building-the-demo.md)\n- [Running The Demo](sections/running-the-demo.md)\n- [Reference Frames](sections/reference-frames.md)\n- [GLSL](sections/glsl.md)\n- [Render To Texture](sections/render-to-texture.md)\n- [Texturing](sections/texturing.md)\n- [Lighting](sections/lighting.md)\n- [Blinn-Phong](sections/blinn-phong.md)\n- [Fresnel Factor](sections/fresnel-factor.md)\n- [Rim Lighting](sections/rim-lighting.md)\n- [Cel Shading](sections/cel-shading.md)\n- [Normal Mapping](sections/normal-mapping.md)\n- [Deferred Rendering](sections/deferred-rendering.md)\n- [Fog](sections/fog.md)\n- [Blur](sections/blur.md)\n- [Bloom](sections/bloom.md)\n- [SSAO](sections/ssao.md)\n- [Motion Blur](sections/motion-blur.md)\n- [Chromatic Aberration](sections/chromatic-aberration.md)\n- [Screen Space Reflection](sections/screen-space-reflection.md)\n- [Screen Space Refraction](sections/screen-space-refraction.md)\n- [Foam](sections/foam.md)\n- [Flow Mapping](sections/flow-mapping.md)\n- [Outlining](sections/outlining.md)\n- [Depth Of Field](sections/depth-of-field.md)\n- [Posterization](sections/posterization.md)\n- [Pixelization](sections/pixelization.md)\n- [Sharpen](sections/sharpen.md)\n- [Dilation](sections/dilation.md)\n- [Film Grain](sections/film-grain.md)\n- [Lookup Table (LUT)](sections/lookup-table.md)\n- [Gamma Correction](sections/gamma-correction.md)\n\n## License\n\nThe included license applies only to the software portion of 3D Game Shaders For Beginners—\nspecifically the `.cxx`, `.vert`, and `.frag` source code files.\nNo other portion of 3D Game Shaders For Beginners has been licensed for use.\n\n## Attributions\n\n- [Kiwi Soda Font](https://fontenddev.com/fonts/kiwi-soda/)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n"
  },
  {
    "path": "demonstration/build-for-linux.sh",
    "content": "#!/usr/bin/env bash\n\nSCRIPT_PATH=\"$(cd \"$(dirname \"$0\")\"; pwd -P)\"\n\ng++ \\\n  -Wfatal-errors \\\n  -c $SCRIPT_PATH/src/main.cxx \\\n  -o $SCRIPT_PATH/3d-game-shaders-for-beginners.o \\\n  -std=gnu++11 \\\n  -O3 \\\n  -I/usr/include/python3.9/ \\\n  -I$P3D_INCLUDE_PATH\n\ng++ \\\n  $SCRIPT_PATH/3d-game-shaders-for-beginners.o \\\n  -o $SCRIPT_PATH/3d-game-shaders-for-beginners \\\n  -L$P3D_LIB_PATH \\\n  -lp3framework \\\n  -lpanda \\\n  -lpandafx \\\n  -lpandaexpress \\\n  -lpandaphysics \\\n  -lp3dtoolconfig \\\n  -lp3dtool \\\n  -lpthread\n"
  },
  {
    "path": "demonstration/panda3d-prc-file.prc",
    "content": "load-display               pandagl\n\naudio-library-name         p3openal_audio\n\nwin-origin                 -2 -2\nwin-size                   1200 900\n\nfullscreen                 #f\n\nframebuffer-hardware       #t\nframebuffer-software       #f\n\nframebuffer-srgb           #f\n\ndepth-bits                 1\ncolor-bits                 1 1 1\nalpha-bits                 0\nstencil-bits               0\nmultisamples               0\n\nnotify-level               warning\ndefault-directnotify-level warning\n\nmodel-path                 $MAIN_DIR\n\nwant-directtools           #f\nwant-tk                    #f\n\nwant-pstats                #f\nshow-frame-rate-meter      #f\n\nuse-movietexture           #t\n\nhardware-animated-vertices #f\n\nmodel-cache-dir            $XDG_CACHE_HOME/panda3d\nmodel-cache-textures       #f\n\nbasic-shaders-only         #f\n\ngl-coordinate-system       default\ngl-version                 3 2\n\ntextures-auto-power-2      1\ntextures-power-2           down\n\nsync-flip                  #f\nsync-video                 #f\n"
  },
  {
    "path": "demonstration/shaders/LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2019, David Lettier\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "demonstration/shaders/fragment/base-combine.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D baseTexture;\nuniform sampler2D refractionTexture;\nuniform sampler2D foamTexture;\nuniform sampler2D reflectionTexture;\nuniform sampler2D specularTexture;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(baseTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 base       = texture(baseTexture,       texCoord);\n  vec4 refraction = texture(refractionTexture, texCoord);\n  vec4 foam       = texture(foamTexture,       texCoord);\n  vec4 reflection = texture(reflectionTexture, texCoord);\n  vec4 specular   = texture(specularTexture,   texCoord);\n\n  fragColor      = base;\n  fragColor.rgb  = mix(fragColor.rgb, refraction.rgb, clamp(refraction.a, 0.0, 1.0));\n  fragColor.rgb  = mix(fragColor.rgb, reflection.rgb, clamp(reflection.a, 0.0, 1.0));\n  fragColor.rgb  = mix(fragColor.rgb, foam.rgb,       clamp(foam.a,       0.0, 1.0));\n  fragColor.rgb += (specular.rgb * clamp(specular.a, 0.0, 1.0));\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/base.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define NUMBER_OF_LIGHTS    4\n#define MAX_SHININESS     127.75\n#define MAX_FRESNEL_POWER   5.0\n\nuniform float osg_FrameTime;\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform mat4 trans_world_to_view;\nuniform mat4 trans_view_to_world;\n\nuniform sampler2D p3d_Texture0;\nuniform sampler2D p3d_Texture1;\nuniform sampler2D p3d_Texture2;\nuniform sampler2D flowTexture;\nuniform sampler2D ssaoBlurTexture;\n\nuniform struct\n  { vec4 ambient\n  ; vec4 diffuse\n  ; vec4 emission\n  ; vec3 specular\n  ; float shininess\n  ;\n  } p3d_Material;\n\nuniform struct\n  { vec4 ambient\n  ;\n  } p3d_LightModel;\n\nuniform struct p3d_LightSourceParameters\n  { vec4 color\n\n  ; vec4 ambient\n  ; vec4 diffuse\n  ; vec4 specular\n\n  ; vec4 position\n\n  ; vec3  spotDirection\n  ; float spotExponent\n  ; float spotCutoff\n  ; float spotCosCutoff\n\n  ; float constantAttenuation\n  ; float linearAttenuation\n  ; float quadraticAttenuation\n\n  ; vec3 attenuation\n\n  ; sampler2DShadow shadowMap\n\n  ; mat4 shadowViewMatrix\n  ;\n  } p3d_LightSource[NUMBER_OF_LIGHTS];\n\nuniform vec2 normalMapsEnabled;\nuniform vec2 fresnelEnabled;\nuniform vec2 rimLightEnabled;\nuniform vec2 blinnPhongEnabled;\nuniform vec2 celShadingEnabled;\nuniform vec2 flowMapsEnabled;\nuniform vec2 specularOnly;\nuniform vec2 isParticle;\nuniform vec2 isWater;\nuniform vec2 sunPosition;\n\nin vec4 vertexColor;\n\nin vec4 vertexInShadowSpaces[NUMBER_OF_LIGHTS];\n\nin vec4 vertexPosition;\n\nin vec3 vertexNormal;\nin vec3 binormal;\nin vec3 tangent;\n\nin vec2 diffuseCoord;\nin vec2 normalCoord;\n\nout vec4 out0;\nout vec4 out1;\n\nvoid main() {\n  vec3  shadowColor   = pow(vec3(0.149, 0.220, 0.227), vec3(gamma.x));\n  int   shadowSamples = 2;\n\n  vec4 diffuseColor;\n  if (isParticle.x == 1) {\n    diffuseColor   = texture(p3d_Texture0, diffuseCoord) * vertexColor;\n  } else {\n    diffuseColor   = texture(p3d_Texture0, diffuseCoord);\n  }\n  diffuseColor.rgb = pow(diffuseColor.rgb, vec3(gamma.x));\n\n  vec3 materialSpecularColor = p3d_Material.specular;\n\n  vec2 flow   = texture(flowTexture, normalCoord).xy;\n       flow   = (flow - 0.5) * 2.0;\n       flow.x = abs(flow.x) <= 0.02 ? 0.0 : flow.x;\n       flow.y = abs(flow.y) <= 0.02 ? 0.0 : flow.y;\n\n  vec4 normalTex =\n    texture\n      ( p3d_Texture1\n      , vec2\n          ( normalCoord.x + flowMapsEnabled.x * flow.x * osg_FrameTime\n          , normalCoord.y + flowMapsEnabled.y * flow.y * osg_FrameTime\n          )\n      );\n\n  vec3 normal;\n\n  if (isParticle.x == 1) {\n    normal = normalize((trans_world_to_view * vec4(0.0, 0.0, 1.0, 0.0)).xyz);\n  } else if (normalMapsEnabled.x == 1) {\n    vec3 normalRaw =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normalRaw\n        );\n  } else {\n    normal =\n      normalize(vertexNormal);\n  }\n\n  vec4 specularMap = texture(p3d_Texture2, diffuseCoord);\n\n  vec4 diffuse  = vec4(0.0, 0.0, 0.0, diffuseColor.a);\n  vec4 specular = vec4(0.0, 0.0, 0.0, diffuseColor.a);\n\n  for (int i = 0; i < p3d_LightSource.length(); ++i) {\n    vec3 lightDirection =\n        p3d_LightSource[i].position.xyz\n      - vertexPosition.xyz\n      * p3d_LightSource[i].position.w;\n\n    vec3 unitLightDirection = normalize(lightDirection);\n    vec3 eyeDirection       = normalize(-vertexPosition.xyz);\n    vec3 reflectedDirection = normalize(-reflect(unitLightDirection, normal));\n    vec3 halfwayDirection   = normalize(unitLightDirection + eyeDirection);\n\n    float lightDistance = length(lightDirection);\n\n    float attenuation =\n        1.0\n      / ( p3d_LightSource[i].constantAttenuation\n        + p3d_LightSource[i].linearAttenuation\n        * lightDistance\n        + p3d_LightSource[i].quadraticAttenuation\n        * (lightDistance * lightDistance)\n        );\n\n    if (attenuation <= 0.0) { continue; }\n\n    float diffuseIntensity = dot(normal, unitLightDirection);\n\n    if (diffuseIntensity < 0.0) { continue; }\n\n    diffuseIntensity =\n        celShadingEnabled.x == 1\n      ? smoothstep(0.1, 0.2, diffuseIntensity)\n      : diffuseIntensity;\n\n    vec4 lightDiffuseColor     = p3d_LightSource[i].diffuse;\n         lightDiffuseColor.rgb = pow(lightDiffuseColor.rgb, vec3(gamma.x));\n\n    vec4 diffuseTemp =\n      vec4\n        ( clamp\n            (   diffuseColor.rgb\n              * lightDiffuseColor.rgb\n              * diffuseIntensity\n            , 0.0\n            , 1.0\n            )\n        , diffuseColor.a\n        );\n\n    float specularIntensity =\n      ( blinnPhongEnabled.x == 1\n      ? clamp(dot(normal,       halfwayDirection),   0.0, 1.0)\n      : clamp(dot(eyeDirection, reflectedDirection), 0.0, 1.0)\n      );\n\n    specularIntensity =\n      ( celShadingEnabled.x == 1\n      ? smoothstep(0.9, 1.0, specularIntensity)\n      : specularIntensity\n      );\n\n    vec4  lightSpecularColor     = p3d_LightSource[i].specular;\n          lightSpecularColor.rgb = pow(lightSpecularColor.rgb, vec3(gamma.x));\n\n    vec4 materialSpecularColor        = vec4(vec3(specularMap.r), diffuseColor.a);\n    if (fresnelEnabled.x == 1) {\n      float fresnelFactor             = dot((blinnPhongEnabled.x == 1 ? halfwayDirection : normal), eyeDirection);\n            fresnelFactor             = max(fresnelFactor, 0.0);\n            fresnelFactor             = 1.0 - fresnelFactor;\n            fresnelFactor             = pow(fresnelFactor, specularMap.b * MAX_FRESNEL_POWER);\n            materialSpecularColor.rgb = mix(materialSpecularColor.rgb, vec3(1.0), clamp(fresnelFactor, 0.0, 1.0));\n    }\n\n    vec4 specularTemp      = vec4(vec3(0.0), diffuseColor.a);\n         specularTemp.rgb  = lightSpecularColor.rgb * pow(specularIntensity, specularMap.g * MAX_SHININESS);\n         specularTemp.rgb *= materialSpecularColor.rgb;\n         specularTemp.rgb *= (1 - isParticle.x);\n         specularTemp.rgb  = clamp(specularTemp.rgb, 0.0, 1.0);\n\n    float unitLightDirectionDelta =\n      dot\n        ( normalize(p3d_LightSource[i].spotDirection)\n        , -unitLightDirection\n        );\n\n    if (unitLightDirectionDelta < p3d_LightSource[i].spotCosCutoff) { continue; }\n\n    float spotExponent = p3d_LightSource[i].spotExponent;\n\n    diffuseTemp.rgb *= (spotExponent <= 0.0 ? 1.0 : pow(unitLightDirectionDelta, spotExponent));\n\n    vec2  shadowMapSize = textureSize(p3d_LightSource[i].shadowMap, 0);\n    float inShadow      = 0.0;\n    float count         = 0.0;\n\n    for (  int si = -shadowSamples; si <= shadowSamples; ++si) {\n      for (int sj = -shadowSamples; sj <= shadowSamples; ++sj) {\n        inShadow +=\n          ( 1.0\n          - textureProj\n              ( p3d_LightSource[i].shadowMap\n              , vertexInShadowSpaces[i] + vec4(vec2(si, sj) / shadowMapSize, vec2(0.0))\n              )\n          );\n\n        count += 1.0;\n      }\n    }\n\n    inShadow /= count;\n\n    vec3 shadow =\n      mix\n        ( vec3(1.0)\n        , shadowColor\n        , inShadow\n        );\n\n    diffuseTemp.rgb  *= mix(shadow, vec3(1.0), isParticle.x);\n    specularTemp.rgb *= mix(shadow, vec3(1.0), isParticle.x);\n\n    diffuseTemp.rgb  *= attenuation;\n    specularTemp.rgb *= attenuation;\n\n    diffuse.rgb  += diffuseTemp.rgb;\n    specular.rgb += specularTemp.rgb;\n  }\n\n  vec4 rimLight = vec4(vec3(0.0), diffuseColor.a);\n  if (rimLightEnabled.x == 1) {\n       rimLight.rgb =\n        vec3\n          ( 1.0\n          - max\n              ( 0.0\n              , dot(normalize(-vertexPosition.xyz), normalize(normal))\n              )\n          );\n       rimLight.rgb =\n          ( celShadingEnabled.x == 1\n          ? smoothstep(0.3, 0.4, rimLight.rgb)\n          : pow(rimLight.rgb, vec3(2.0)) * 1.2\n          );\n       rimLight.rgb *= diffuse.rgb;\n  }\n\n  vec2 ssaoBlurTexSize  = textureSize(ssaoBlurTexture, 0).xy;\n  vec2 ssaoBlurTexCoord = gl_FragCoord.xy / ssaoBlurTexSize;\n  vec3 ssao             = texture(ssaoBlurTexture, ssaoBlurTexCoord).rgb;\n       ssao             = mix(shadowColor, vec3(1.0), clamp(ssao.r, 0.0, 1.0));\n\n  float sunPosition  = sin(sunPosition.x * pi.y);\n  float sunMixFactor = 1.0 - (sunPosition / 2.0 + 0.5);\n\n  vec3 ambientCool = pow(vec3(0.302, 0.451, 0.471), vec3(gamma.x)) * max(0.5, sunMixFactor);\n  vec3 ambientWarm = pow(vec3(0.765, 0.573, 0.400), vec3(gamma.x)) * max(0.5, sunMixFactor);\n\n  vec3 skyLight    = mix(ambientCool, ambientWarm, sunMixFactor);\n  vec3 groundLight = mix(ambientWarm, ambientCool, sunMixFactor);\n\n  vec3 worldNormal = normalize((trans_view_to_world * vec4(normal, 0.0)).xyz);\n\n  vec3 ambientLight =\n    mix\n      ( groundLight\n      , skyLight\n      , 0.5 * (1.0 + dot(worldNormal, vec3(0, 0, 1)))\n      );\n\n  vec3 ambient =\n      ambientLight.rgb\n    * diffuseColor.rgb\n    * ssao;\n\n  vec3 emission = p3d_Material.emission.rgb * max(0.1, pow(sunPosition, 0.4));\n\n  out0.a   =   diffuseColor.a;\n  out0.rgb =   ambient.rgb\n             + diffuse.rgb\n             + rimLight.rgb\n             + emission.rgb;\n\n  if (isWater.x == 1) { out0.a = 0.0; }\n\n  out1.a   = diffuseColor.a;\n  out1.rgb = specular.rgb;\n\n  if (isParticle.x == 1) { out1.rgb = vec3(0.0); }\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/bloom.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  int   size       = 3;\n  float separation = 4.0;\n  float threshold  = 0.6;\n  float amount     = 0.6;\n\n  if (enabled.x != 1 || size <= 0) { fragColor = vec4(0); return; }\n\n  vec2 texSize = textureSize(colorTexture, 0).xy;\n\n  vec4 result = vec4(0.0);\n  vec4 color  = vec4(0.0);\n\n  float value = 0.0;\n  float count = 0.0;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      color =\n        texture\n          ( colorTexture\n          ,   (vec2(i, j) * separation + gl_FragCoord.xy)\n            / texSize\n          );\n\n      value = max(color.r, max(color.g, color.b));\n      if (value < threshold) { color = vec4(0.0); }\n\n      result += color;\n      count  += 1.0;\n    }\n  }\n\n  result /= count;\n\n  fragColor = mix(vec4(0.0), result, amount);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/box-blur.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\n\nuniform vec2 parameters;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  fragColor = texture(colorTexture, texCoord);\n\n  int size = int(parameters.x);\n  if (size <= 0) { return; }\n\n  float separation = parameters.y;\n        separation = max(separation, 1);\n\n  fragColor.rgb = vec3(0);\n\n  float count = 0.0;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      fragColor.rgb +=\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + (vec2(i, j) * separation)\n              )\n            / texSize\n          ).rgb;\n\n      count += 1.0;\n    }\n  }\n\n  fragColor.rgb /= count;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/chromatic-aberration.frag",
    "content": "/*\n  (C) 2021 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\n\nuniform vec2 enabled;\nuniform vec2 mouseFocusPoint;\n\nout vec4 fragColor;\n\nvoid main() {\n  float redOffset   =  0.009;\n  float greenOffset =  0.006;\n  float blueOffset  = -0.006;\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec2 direction = texCoord - mouseFocusPoint;\n\n  fragColor = texture(colorTexture, texCoord);\n\n  if (enabled.x != 1) { return; }\n\n  fragColor.r = texture(colorTexture, texCoord + (direction * vec2(redOffset  ))).r;\n  fragColor.g = texture(colorTexture, texCoord + (direction * vec2(greenOffset))).g;\n  fragColor.b = texture(colorTexture, texCoord + (direction * vec2(blueOffset ))).b;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/depth-of-field.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D positionTexture;\nuniform sampler2D noiseTexture;\nuniform sampler2D focusTexture;\nuniform sampler2D outOfFocusTexture;\n\nuniform vec2 mouseFocusPoint;\nuniform vec2 nearFar;\nuniform vec2 enabled;\n\nout vec4 fragColor;\nout vec4 fragColor1;\n\nvoid main() {\n  float minDistance =  8.0;\n  float maxDistance = 12.0;\n\n  float far  = nearFar.y;\n\n  vec2 texSize  = textureSize(focusTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 focusColor = texture(focusTexture, texCoord);\n\n  fragColor = focusColor;\n\n  if (enabled.x != 1) { return; }\n\n  vec4 position = texture(positionTexture, texCoord);\n\n  if (position.a <= 0) { fragColor1 = vec4(1.0); return; }\n\n  vec4 outOfFocusColor = texture(outOfFocusTexture, texCoord);\n  vec4 focusPoint      = texture(positionTexture,   mouseFocusPoint);\n\n  float blur =\n    smoothstep\n      ( minDistance\n      , maxDistance\n      , length(position - focusPoint)\n      );\n\n  fragColor  = mix(focusColor, outOfFocusColor, blur);\n  fragColor1 = vec4(blur);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/dilation.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\n\nuniform vec2 parameters;\n\nout vec4 fragColor;\n\nvoid main() {\n  int   size         = int(parameters.x);\n  float separation   =     parameters.y;\n  float minThreshold = 0.2;\n  float maxThreshold = 0.5;\n\n  vec2 texSize   = textureSize(colorTexture, 0).xy;\n  vec2 fragCoord = gl_FragCoord.xy;\n\n  fragColor = texture(colorTexture, fragCoord / texSize);\n\n  if (size <= 0) { return; }\n\n  float  mx = 0.0;\n  vec4  cmx = fragColor;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      // For a rectangular shape.\n      //if (false);\n\n      // For a diamond shape;\n      //if (!(abs(i) <= size - abs(j))) { continue; }\n\n      // For a circular shape.\n      if (!(distance(vec2(i, j), vec2(0, 0)) <= size)) { continue; }\n\n      vec4 c =\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + (vec2(i, j) * separation)\n              )\n            / texSize\n          );\n\n      float mxt = dot(c.rgb, vec3(0.3, 0.59, 0.11));\n\n      if (mxt > mx) {\n         mx = mxt;\n        cmx = c;\n      }\n    }\n  }\n\n  fragColor.rgb =\n    mix\n      ( fragColor.rgb\n      , cmx.rgb\n      , smoothstep(minThreshold, maxThreshold, mx)\n      );\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/discard.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nvoid main() {\n  discard;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/film-grain.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\n\nuniform float osg_FrameTime;\n\nuniform sampler2D colorTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float amount = 0.01;\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 color = texture(colorTexture, texCoord);\n\n  if (enabled.x == 1) {\n    float randomIntensity =\n      fract\n        ( 10000\n        * sin\n            (\n              ( gl_FragCoord.x\n              + gl_FragCoord.y\n              * osg_FrameTime\n              )\n            * pi.y\n            )\n        );\n\n    amount *= randomIntensity;\n\n    color.rgb += amount;\n  }\n\n  fragColor = color;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/foam-mask.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform float osg_FrameTime;\n\nuniform sampler2D foamPatternTexture;\nuniform sampler2D flowTexture;\n\nuniform vec2 flowMapsEnabled;\n\nin vec2 diffuseCoord;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 flow   = texture(flowTexture, diffuseCoord).xy;\n       flow   = (flow - 0.5) * 2;\n       flow.x = abs(flow.x) <= 0.02 ? 0 : flow.x;\n       flow.y = abs(flow.y) <= 0.02 ? 0 : flow.y;\n\n  vec4 foamPattern =\n    texture\n      ( foamPatternTexture\n      , vec2\n          ( diffuseCoord.x + flowMapsEnabled.x * flow.x * osg_FrameTime\n          , diffuseCoord.y + flowMapsEnabled.y * flow.y * osg_FrameTime\n          )\n      );\n\n  fragColor = vec4(vec3(dot(foamPattern.rgb, vec3(1)) / 3), 1);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/foam.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform mat4 viewWorldMat;\n\nuniform sampler2D maskTexture;\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\n\nuniform vec2 foamDepth;\nuniform vec2 sunPosition;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec4 foamColor = vec4(0.8, 0.85, 0.92, 0.8);\n\n  vec2 texSize  = textureSize(positionFromTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 mask = texture(maskTexture, texCoord);\n\n  if (mask.r <= 0.0 || foamDepth.x <= 0.0) { fragColor = vec4(0.0); return; }\n\n  foamColor.rgb  = pow(foamColor.rgb, vec3(gamma.x));\n  foamColor.rgb *= max(0.4, -1 * sin(sunPosition.x * pi.y));\n\n  vec4 positionFrom = texture(positionFromTexture, texCoord);\n  vec4 positionTo   = texture(positionToTexture,   texCoord);\n\n  positionFrom = viewWorldMat * positionFrom;\n  positionTo   = viewWorldMat * positionTo;\n\n  float depth   = length(positionTo.xyz - positionFrom.xyz);\n  float amount  = clamp(depth / foamDepth.x, 0.0, 1.0);\n        amount  = 1.0 - amount;\n        amount *= mask.r;\n        // Ease in and out.\n        amount  =   (amount * amount)\n                  / (2.0 * (amount * amount - amount) + 1.0);\n\n  fragColor = vec4(foamColor.rgb, amount * foamColor.a);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/fog.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform vec4 backgroundColor0;\nuniform vec4 backgroundColor1;\n\nuniform sampler2D positionTexture0;\nuniform sampler2D positionTexture1;\nuniform sampler2D smokeMaskTexture;\n\nuniform vec3 origin;\nuniform vec2 nearFar;\nuniform vec2 sunPosition;\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float fogMin = 0.00;\n  float fogMax = 0.97;\n\n  if (enabled.x != 1) { fragColor = vec4(0); return; }\n\n  vec2 texSize  = textureSize(positionTexture0, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 smokeMask    = texture(smokeMaskTexture, texCoord);\n  vec4 position0    = texture(positionTexture0, texCoord);\n       position0.y -= origin.y;\n\n  float near = nearFar.x;\n  float far  = nearFar.y;\n\n  vec4 position1    = texture(positionTexture1, texCoord);\n       position1.y -= origin.y;\n  if (position1.a <= 0) { position1.y = far; }\n\n  vec4 position = position1;\n\n  if        (position0.a <= 0 && smokeMask.r > 0) {\n    position.y   = mix(far,           position1.y,   smokeMask.r);\n  } else if (position0.a >  0 && smokeMask.r > 0) {\n    position.xyz = mix(position0.xyz, position1.xyz, smokeMask.r);\n  }\n\n  float random =\n    fract\n      ( 10000\n      * sin\n          (\n            ( gl_FragCoord.x\n            * 104729\n            + gl_FragCoord.y\n            * 7639\n            )\n          * pi.y\n          )\n      );\n\n  vec4 backgroundColor0     = backgroundColor0;\n  vec4 backgroundColor1     = backgroundColor1;\n       backgroundColor0.rgb = pow(backgroundColor0.rgb, vec3(gamma.x));\n       backgroundColor1.rgb = pow(backgroundColor1.rgb, vec3(gamma.x));\n\n  vec4 color =\n    mix\n      ( backgroundColor0\n      , backgroundColor1\n      , 1.0 - clamp(random * 0.1 + texCoord.y, 0.0, 1.0)\n      );\n\n  float sunPosition = max(0.2, -1 * sin(sunPosition.x * pi.y));\n\n  color.rgb *= sunPosition;\n  color.b    = mix(color.b + 0.05, color.b, sunPosition);\n\n  float intensity =\n    clamp\n      (   (position.y - near)\n        / (far        - near)\n      , fogMin\n      , fogMax\n      );\n\n  fragColor = vec4(color.rgb, intensity);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/gamma-correction.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 gamma;\n\nuniform sampler2D colorTexture;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  fragColor     = texture(colorTexture, texCoord);\n  fragColor.rgb = pow(fragColor.rgb, vec3(gamma.y));\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/geometry-buffer-0.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D p3d_Texture1;\n\nuniform vec2 normalMapsEnabled;\n\nin vec4 vertexPosition;\n\nin vec3 vertexNormal;\nin vec3 binormal;\nin vec3 tangent;\n\nin vec2 normalCoord;\n\nout vec4 positionOut;\nout vec4 normalOut;\n\nvoid main() {\n  vec4 normalTex =\n    texture\n      ( p3d_Texture1\n      , normalCoord\n      );\n\n  vec3 normal;\n  if (normalMapsEnabled.x == 1) {\n    normal =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normal\n        );\n  } else {\n    normal = normalize(vertexNormal);\n  }\n\n  positionOut = vertexPosition;\n  normalOut   = vec4(normal, 1);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/geometry-buffer-1.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define MAX_SHININESS 127.75\n\nuniform float osg_FrameTime;\n\nuniform struct\n  { vec3 specular\n  ; float shininess\n  ;\n  } p3d_Material;\n\nuniform mat4 p3d_ProjectionMatrix;\n\nuniform sampler2D p3d_Texture1;\nuniform sampler2D p3d_Texture3;\nuniform sampler2D p3d_Texture4;\nuniform sampler2D flowTexture;\nuniform sampler2D foamPatternTexture;\n\nuniform vec2 normalMapsEnabled;\nuniform vec2 flowMapsEnabled;\n\nin vec4 vertexPosition;\nin vec4 vertexColor;\n\nin vec3 vertexNormal;\nin vec3 binormal;\nin vec3 tangent;\n\nin vec2 diffuseCoord;\nin vec2 normalCoord;\n\nout vec4 positionOut;\nout vec4 normalOut;\nout vec4 reflectionMaskOut;\nout vec4 refractionMaskOut;\nout vec4 foamMaskOut;\n\nvoid main() {\n  vec2 flow   = texture(flowTexture,  normalCoord).xy;\n       flow   = (flow - 0.5) * 2.0;\n       flow.x = abs(flow.x) <= 0.02 ? 0.0 : flow.x;\n       flow.y = abs(flow.y) <= 0.02 ? 0.0 : flow.y;\n\n  vec3 normal;\n\n  if (normalMapsEnabled.x == 1) {\n    vec4 normalTex =\n      texture\n        ( p3d_Texture1\n        , vec2\n            ( normalCoord.x + flowMapsEnabled.x * flow.x * osg_FrameTime\n            , normalCoord.y + flowMapsEnabled.y * flow.y * osg_FrameTime\n            )\n        );\n\n    normal =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normal\n        );\n  } else {\n    normal = normalize(vertexNormal);\n  }\n\n  vec4 reflectionMask = texture(p3d_Texture3, diffuseCoord);\n  vec4 refractionMask = texture(p3d_Texture4, diffuseCoord);\n\n  vec2 foamPatternTextureSize = textureSize(foamPatternTexture, 0).xy;\n\n  vec4 foamUvOffset           = p3d_ProjectionMatrix * vec4(normalize(normal), 1.0);\n       foamUvOffset.xyz      /= foamUvOffset.w;\n       foamUvOffset.xy        = foamUvOffset.xy * 0.5 + 0.5;\n       foamUvOffset.xy       /= foamPatternTextureSize;\n       foamUvOffset.xy       *= 0.3;\n\n  vec2 foamUv                 = diffuseCoord.xy + foamUvOffset.xy;\n       foamUv                 = foamUv + flowMapsEnabled * flow * 0.5 * osg_FrameTime;\n       foamUv                *= 0.5;\n\n  vec4 foamPattern            = texture(foamPatternTexture, foamUv);\n\n  positionOut         = vertexPosition;\n  normalOut           = vec4(normal, 1.0);\n  reflectionMaskOut   = reflectionMask;\n  refractionMaskOut   = refractionMask;\n  foamMaskOut         = foamPattern;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/geometry-buffer-2.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n\nuniform sampler2D p3d_Texture0;\nuniform sampler2D positionTexture;\n\nuniform vec2 isSmoke;\n\nin vec4 vertexPosition;\nin vec4 vertexColor;\n\nin vec2 diffuseCoord;\n\nout vec4 positionOut;\nout vec4 smokeMaskOut;\n\nvoid main() {\n  positionOut  = vertexPosition;\n  smokeMaskOut = vec4(0.0);\n\n  if (isSmoke.x == 1) {\n    vec4 diffuseColor = texture(p3d_Texture0, diffuseCoord) * vertexColor;\n\n    vec2 texSize  = textureSize(positionTexture, 0).xy;\n    vec2 texCoord = gl_FragCoord.xy / texSize;\n\n    vec4 position = texture(positionTexture, texCoord);\n    if (position.a <= 0.0) {\n      positionOut         = diffuseColor.a > 0.0 ? vertexPosition : vec4(0.0);\n    } else {\n      positionOut         = mix(position, vertexPosition, diffuseColor.a);\n    }\n\n    smokeMaskOut     = diffuseColor * vertexColor;\n    smokeMaskOut.rgb = vec3(dot(smokeMaskOut.rgb, vec3(1.0 / 3.0)));\n  }\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/kuwahara-filter.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define MAX_SIZE        5\n#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))\n\nuniform sampler2D colorTexture;\n\nuniform vec2 parameters;\n\nout vec4 fragColor;\n\nvec2 texSize  = textureSize(colorTexture, 0).xy;\nvec2 texCoord = gl_FragCoord.xy / texSize;\n\nint i     = 0;\nint j     = 0;\nint count = 0;\n\nvec3  valueRatios = vec3(0.3, 0.59, 0.11);\n\nfloat values[MAX_KERNEL_SIZE];\n\nvec4  color       = vec4(0.0);\nvec4  meanTemp    = vec4(0.0);\nvec4  mean        = vec4(0.0);\nfloat valueMean   =  0.0;\nfloat variance    =  0.0;\nfloat minVariance = -1.0;\n\nvoid findMean(int i0, int i1, int j0, int j1) {\n  meanTemp = vec4(0);\n  count    = 0;\n\n  for (i = i0; i <= i1; ++i) {\n    for (j = j0; j <= j1; ++j) {\n      color  =\n        texture\n          ( colorTexture\n          ,   (gl_FragCoord.xy + vec2(i, j))\n            / texSize\n          );\n\n      meanTemp += color;\n\n      values[count] = dot(color.rgb, valueRatios);\n\n      count += 1;\n    }\n  }\n\n  meanTemp.rgb /= count;\n  valueMean     = dot(meanTemp.rgb, valueRatios);\n\n  for (i = 0; i < count; ++i) {\n    variance += pow(values[i] - valueMean, 2);\n  }\n\n  variance /= count;\n\n  if (variance < minVariance || minVariance <= -1) {\n    mean = meanTemp;\n    minVariance = variance;\n  }\n}\n\nvoid main() {\n  fragColor = texture(colorTexture, texCoord);\n\n  int size = int(parameters.x);\n  if (size <= 0) { return; }\n\n  // Lower Left\n\n  findMean(-size, 0, -size, 0);\n\n  // Upper Right\n\n  findMean(0, size, 0, size);\n\n  // Upper Left\n\n  findMean(-size, 0, 0, size);\n\n  // Lower Right\n\n  findMean(0, size, -size, 0);\n\n  fragColor.rgb = mean.rgb;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/lookup-table.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform sampler2D colorTexture;\nuniform sampler2D lookupTableTexture0;\nuniform sampler2D lookupTableTexture1;\n\nuniform vec2 sunPosition;\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize = textureSize(colorTexture, 0).xy;\n\n  vec4 color = texture(colorTexture, gl_FragCoord.xy / texSize);\n\n  if (enabled.x != 1) { fragColor = color; return; }\n\n  color.rgb = pow(color.rgb, vec3(gamma.y));\n\n  float u  =  floor(color.b * 15.0) / 15.0 * 240.0;\n        u  = (floor(color.r * 15.0) / 15.0 *  15.0) + u;\n        u /= 255.0;\n  float v  = 1.0 - (floor(color.g * 15.0) / 15.0);\n\n  vec3 left0 = texture(lookupTableTexture0, vec2(u, v)).rgb;\n  vec3 left1 = texture(lookupTableTexture1, vec2(u, v)).rgb;\n\n  u  =  ceil(color.b * 15.0) / 15.0 * 240.0;\n  u  = (ceil(color.r * 15.0) / 15.0 *  15.0) + u;\n  u /= 255.0;\n  v  = 1.0 - (ceil(color.g * 15.0) / 15.0);\n\n  vec3 right0 = texture(lookupTableTexture0, vec2(u, v)).rgb;\n  vec3 right1 = texture(lookupTableTexture1, vec2(u, v)).rgb;\n\n  float sunPosition = sin(sunPosition.x * pi.y);\n        sunPosition = 0.5 * (sunPosition + 1);\n\n  vec3 left  = mix(left0,  left1,  sunPosition);\n  vec3 right = mix(right0, right1, sunPosition);\n\n  color.r = mix(left.r, right.r, fract(color.r * 15.0));\n  color.g = mix(left.g, right.g, fract(color.g * 15.0));\n  color.b = mix(left.b, right.b, fract(color.b * 15.0));\n\n  color.rgb = pow(color.rgb, vec3(gamma.x));\n\n  fragColor = color;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/material-diffuse.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform struct\n  { vec4 diffuse\n  ;\n  } p3d_Material;\n\nout vec4 fragColor;\n\nvoid main() {\n  fragColor = p3d_Material.diffuse;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/material-specular.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define MAX_SHININESS 127.75\n\nuniform struct\n  { vec3 specular\n  ; float shininess\n  ;\n  } p3d_Material;\n\nout vec4 fragColor;\n\nvoid main() {\n  fragColor =\n    vec4\n      ( p3d_Material.specular\n      , clamp(p3d_Material.shininess / MAX_SHININESS, 0.0, 1.0)\n      );\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/median-filter.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define MAX_SIZE        4\n#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))\n#define MAX_BINS_SIZE   10\n\nuniform sampler2D colorTexture;\n\nuniform vec2 parameters;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  int size = int(parameters.x);\n  if (size <= 0) { fragColor = texture(colorTexture, texCoord); return; }\n  if (size > MAX_SIZE) { size = MAX_SIZE; }\n  int kernelSize = int(pow(size * 2 + 1, 2));\n\n  int binsSize = int(parameters.y);\n      binsSize = clamp(binsSize, 1, MAX_BINS_SIZE);\n\n  int i        = 0;\n  int j        = 0;\n  int count    = 0;\n  int binIndex = 0;\n\n  vec4  colors[MAX_KERNEL_SIZE];\n  float bins[MAX_BINS_SIZE];\n  int   binIndexes[colors.length()];\n\n  float total = 0;\n  float limit = floor(float(kernelSize) / 2) + 1;\n\n  float value       = 0;\n  vec3  valueRatios = vec3(0.3, 0.59, 0.11);\n\n  for (i = -size; i <= size; ++i) {\n    for (j = -size; j <= size; ++j) {\n      colors[count] =\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + vec2(i, j)\n              )\n            / texSize\n          );\n      count += 1;\n    }\n  }\n\n  for (i = 0; i < binsSize; ++i) {\n    bins[i] = 0;\n  }\n\n  for (i = 0; i < kernelSize; ++i) {\n    value           = dot(colors[i].rgb, valueRatios);\n    binIndex        = int(floor(value * binsSize));\n    binIndex        = clamp(binIndex, 0, binsSize - 1);\n    bins[binIndex] += 1;\n    binIndexes[i]   = binIndex;\n  }\n\n  binIndex = 0;\n\n  for (i = 0; i < binsSize; ++i) {\n    total += bins[i];\n    if (total >= limit) {\n      binIndex = i;\n      break;\n    }\n  }\n\n  fragColor = colors[0];\n\n  for (i = 0; i < kernelSize; ++i) {\n    if (binIndexes[i] == binIndex) {\n      fragColor = colors[i];\n      break;\n    }\n  }\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/motion-blur.frag",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D positionTexture;\nuniform sampler2D colorTexture;\n\nuniform mat4 previousViewWorldMat;\nuniform mat4 worldViewMat;\nuniform mat4 lensProjection;\n\nuniform vec2 motionBlurEnabled;\nuniform vec2 parameters;\n\nout vec4 fragColor;\n\nvoid main() {\n  int   size       = int(parameters.x);\n  float separation =     parameters.y;\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n       fragColor = texture(colorTexture,    texCoord);\n  vec4 position1 = texture(positionTexture, texCoord);\n\n  if (size <= 0 || separation <= 0.0 || motionBlurEnabled.x != 1 || position1.a <= 0.0) { return; }\n\n  vec4 position0 = worldViewMat * previousViewWorldMat * position1;\n\n  position0      = lensProjection * position0;\n  position0.xyz /= position0.w;\n  position0.xy   = position0.xy * 0.5 + 0.5;\n\n  position1      = lensProjection * position1;\n  position1.xyz /= position1.w;\n  position1.xy   = position1.xy * 0.5 + 0.5;\n\n  vec2 direction = position1.xy - position0.xy;\n\n  if (length(direction) <= 0.0) { return; }\n\n  direction.xy *= separation;\n\n  vec2 forward  = texCoord;\n  vec2 backward = texCoord;\n\n  float count = 1.0;\n\n  for (int i = 0; i < size; ++i) {\n    forward  += direction;\n    backward -= direction;\n\n    fragColor +=\n      texture\n        ( colorTexture\n        , forward\n        );\n    fragColor +=\n      texture\n        ( colorTexture\n        , backward\n        );\n\n    count += 2.0;\n  }\n\n  fragColor /= count;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/normal.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform float osg_FrameTime;\n\nuniform sampler2D p3d_Texture1;\nuniform sampler2D flowTexture;\n\nuniform vec2 normalMapsEnabled;\nuniform vec2 flowMapsEnabled;\n\nin vec3 vertexNormal;\nin vec3 binormal;\nin vec3 tangent;\n\nin vec2 normalCoord;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 flow   = texture(flowTexture,  normalCoord).xy;\n       flow   = (flow - 0.5) * 2;\n       flow.x = abs(flow.x) <= 0.02 ? 0 : flow.x;\n       flow.y = abs(flow.y) <= 0.02 ? 0 : flow.y;\n\n  vec4 normalTex =\n    texture\n      ( p3d_Texture1\n      , vec2\n          ( normalCoord.x + flowMapsEnabled.x * flow.x * osg_FrameTime\n          , normalCoord.y + flowMapsEnabled.y * flow.y * osg_FrameTime\n          )\n      );\n\n  vec3 normal;\n  if (normalMapsEnabled.x == 1) {\n    normal =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normal\n        );\n  } else {\n    normal = normalize(vertexNormal);\n  }\n\n  // To convert Panda3D z-up to OpenGL y-up.\n  // fragColor = vec4(normal.x, normal.z, -normal.y, 1);\n\n  fragColor = vec4(normal, 1);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/outline.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 gamma;\n\nuniform sampler2D positionTexture;\nuniform sampler2D colorTexture;\nuniform sampler2D noiseTexture;\nuniform sampler2D depthOfFieldTexture;\nuniform sampler2D fogTexture;\n\nuniform vec2 nearFar;\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float minSeparation = 1.0;\n  float maxSeparation = 1.0;\n  float minDistance   = 1.5;\n  float maxDistance   = 2.0;\n  float noiseScale    = 1.0;\n  int   size          = 1;\n  vec3  colorModifier = vec3(0.522, 0.431, 0.349);\n\n  colorModifier = pow(colorModifier, vec3(gamma.x));\n\n  float near = nearFar.x;\n  float far  = nearFar.y;\n\n  vec2 fragCoord = gl_FragCoord.xy;\n\n  vec2 texSize   = textureSize(colorTexture, 0).xy;\n  vec2 texCoord  = fragCoord / texSize;\n\n  vec4  color        = texture(colorTexture,        texCoord);\n  float depthOfField = texture(depthOfFieldTexture, texCoord).r;\n  float fog          = texture(fogTexture,          texCoord).a;\n\n  if (enabled.x != 1) { fragColor = color; return; }\n\n  fragColor = vec4(0.0);\n\n  vec2 noise  = texture(noiseTexture, fragCoord / textureSize(noiseTexture, 0).xy).rb;\n       noise  = noise * 2.0 - 1.0;\n       noise *= noiseScale;\n\n  texCoord = (fragCoord - noise) / texSize;\n\n  vec4 position     = texture(positionTexture, texCoord);\n  vec4 positionTemp = position;\n\n  if (position.a <= 0.0) { position.y = far; }\n\n  float depth =\n    clamp\n      (   1.0\n        - ( (far - position.y)\n          / (far - near)\n          )\n      , 0.0\n      , 1.0\n      );\n\n  float separation = mix(maxSeparation, minSeparation, depth);\n  float count      = 1.0;\n  float mx         = 0.0;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      texCoord =\n          (vec2(i, j) * separation + (fragCoord + noise))\n        / texSize;\n\n      positionTemp =\n        texture\n          ( positionTexture\n          , texCoord\n          );\n\n      if (positionTemp.y <= 0.0) { positionTemp.y = far; }\n\n      mx = max(mx, abs(position.y - positionTemp.y));\n\n      depthOfField =\n        max\n          ( texture\n              ( depthOfFieldTexture\n              , texCoord\n              ).r\n          , depthOfField\n          );\n\n      fog +=\n        texture\n          ( fogTexture\n          , texCoord\n          ).a;\n\n      count += 1.0;\n    }\n  }\n\n        depthOfField = 1.0 - clamp(depthOfField, 0.0, 1.0);\n        fog          = 1.0 - clamp(fog / count,  0.0, 1.0);\n  float diff         = smoothstep(minDistance, maxDistance, mx) * depthOfField * fog;\n\n  texCoord = fragCoord / texSize;\n\n  vec3 lineColor  = texture(colorTexture, texCoord).rgb;\n       lineColor *= colorModifier;\n\n  fragColor.rgb = mix(color.rgb, lineColor, clamp(diff, 0.0, 1.0));\n  fragColor.a   = 1.0;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/pixelize.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\nuniform sampler2D positionTexture;\n\nuniform vec2 parameters;\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  // Must be odd.\n  int pixelSize = int(parameters.x);\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  // Avoid the background.\n  vec4 position = texture(positionTexture, texCoord);\n\n  if (enabled.x != 1 || position.a <= 0.0) {\n    fragColor = texture(colorTexture, texCoord);\n\n    return;\n  }\n\n  float x = int(gl_FragCoord.x) % pixelSize;\n  float y = int(gl_FragCoord.y) % pixelSize;\n\n        x = floor(pixelSize / 2.0) - x;\n        y = floor(pixelSize / 2.0) - y;\n\n        x = gl_FragCoord.x + x;\n        y = gl_FragCoord.y + y;\n\n  vec2 uv = vec2(x, y) / texSize;\n\n  fragColor = texture(colorTexture, uv);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/position.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nin vec4 vertexPosition;\n\nout vec4 fragColor;\n\nvoid main() {\n  // To convert Panda3D z-up to OpenGL y-up.\n  /*\n  fragColor = vertexPosition.xzyw;\n  */\n\n  fragColor = vertexPosition;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/posterize.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 gamma;\n\nuniform sampler2D colorTexture;\nuniform sampler2D positionTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float levels = 6.0;\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  // Avoid the background.\n  vec4 position = texture(positionTexture, texCoord);\n  if (position.a <= 0) { fragColor = vec4(0); return; }\n\n  fragColor = texture(colorTexture, texCoord);\n\n  if (enabled.x != 1) { return; }\n\n  fragColor.rgb = pow(fragColor.rgb, vec3(gamma.y));\n\n  float greyscale = max(fragColor.r, max(fragColor.g, fragColor.b));\n\n  float lower     = floor(greyscale * levels) / levels;\n  float lowerDiff = abs(greyscale - lower);\n\n  float upper     = ceil(greyscale * levels) / levels;\n  float upperDiff = abs(upper - greyscale);\n\n  float level      = lowerDiff <= upperDiff ? lower : upper;\n  float adjustment = level / greyscale;\n\n  fragColor.rgb = fragColor.rgb * adjustment;\n\n  fragColor.rgb = pow(fragColor.rgb, vec3(gamma.x));\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/reflection-color.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D uvTexture;\nuniform sampler2D colorTexture;\n\nout vec4 fragColor;\n\nvoid main() {\n  int   size       = 6;\n  float separation = 2.0;\n\n  vec2 texSize  = textureSize(uvTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv = texture(uvTexture, texCoord);\n\n  // Removes holes in the UV map.\n  if (uv.b <= 0.0) {\n          uv    = vec4(0.0);\n    float count = 0.0;\n\n    for (int i = -size; i <= size; ++i) {\n      for (int j = -size; j <= size; ++j) {\n        uv    += texture\n                  ( uvTexture\n                  , ( (vec2(i, j) * separation)\n                    + gl_FragCoord.xy\n                    )\n                    / texSize\n                  );\n        count += 1.0;\n      }\n    }\n\n    uv.xyz /= count;\n  }\n\n  if (uv.b <= 0.0) { fragColor = vec4(0.0); return;}\n\n  vec4  color = texture(colorTexture, uv.xy);\n  float alpha = clamp(uv.b, 0.0, 1.0);\n\n  fragColor = vec4(mix(vec3(0.0), color.rgb, alpha), alpha);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/reflection.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\nuniform sampler2D colorBlurTexture;\nuniform sampler2D maskTexture;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 mask      = texture(maskTexture,      texCoord);\n  vec4 color     = texture(colorTexture,     texCoord);\n  vec4 colorBlur = texture(colorBlurTexture, texCoord);\n\n  float amount = clamp(mask.r, 0.0, 1.0);\n\n  if (amount <= 0.0) { fragColor = vec4(0.0); return; }\n\n  float roughness = clamp(mask.g, 0.0, 1.0);\n\n  fragColor = mix(color, colorBlur, roughness) * amount;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/refraction.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform sampler2D uvTexture;\nuniform sampler2D maskTexture;\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\nuniform sampler2D backgroundColorTexture;\n\nuniform vec2 sunPosition;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec4  tintColor = vec4(0.392, 0.537, 0.561, 0.8);\n  float depthMax  = 2.0;\n\n  vec2 texSize  = textureSize(backgroundColorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 backgroundColor = texture(backgroundColorTexture, texCoord);\n\n  vec4 mask = texture(maskTexture, texCoord);\n  if (mask.r <= 0) { fragColor = backgroundColor; return; }\n\n  vec4 uv   = texture(uvTexture, texCoord);\n  if (uv.b   <= 0) { fragColor = backgroundColor; return; }\n\n  tintColor.rgb  = pow(tintColor.rgb, vec3(gamma.x));\n  tintColor.rgb *= max(0.2, -1 * sin(sunPosition.x * pi.y));\n\n  vec4 positionFrom    = texture(positionFromTexture,    texCoord);\n  vec4 positionTo      = texture(positionToTexture,      uv.xy);\n       backgroundColor = texture(backgroundColorTexture, uv.xy);\n\n  float depth   = length(positionTo.xyz - positionFrom.xyz);\n  float mixture = clamp(depth / depthMax, 0.0, 1.0);\n\n  vec3 shallowColor    = backgroundColor.rgb;\n  vec3 deepColor       = mix(shallowColor, tintColor.rgb, tintColor.a);\n  vec3 foregroundColor = mix(shallowColor, deepColor,     mixture);\n\n  fragColor = mix(vec4(0.0), vec4(foregroundColor, 1.0), uv.b);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/scene-combine.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform vec2 pi;\nuniform vec2 gamma;\n\nuniform sampler2D baseTexture;\nuniform sampler2D bloomTexture;\nuniform sampler2D fogTexture;\n\nuniform vec4 backgroundColor0;\nuniform vec4 backgroundColor1;\n\nuniform vec2 sunPosition;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec2 texSize  = textureSize(baseTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 backgroundColor0     = backgroundColor0;\n  vec4 backgroundColor1     = backgroundColor1;\n       backgroundColor0.rgb = pow(backgroundColor0.rgb, vec3(gamma.x));\n       backgroundColor1.rgb = pow(backgroundColor1.rgb, vec3(gamma.x));\n\n  float random =\n    fract\n      ( 10000\n      * sin\n          (\n            ( gl_FragCoord.x\n            * 104729\n            + gl_FragCoord.y\n            * 7639\n            )\n          * pi.y\n          )\n      );\n\n  float sunPosition = sin(sunPosition.x * pi.y);\n        sunPosition = max(0.2, -1 * sunPosition);\n\n  vec4 backgroundColor =\n    mix\n      ( backgroundColor0\n      , backgroundColor1\n      , 1.0 - clamp(random * 0.1 + texCoord.y, 0.0, 1.0)\n      );\n\n  backgroundColor.rgb *= sunPosition;\n  backgroundColor.b    = mix(backgroundColor.b + 0.05, backgroundColor.b, sunPosition);\n\n  vec4 baseColor  = texture(baseTexture,  texCoord);\n  vec4 bloomColor = texture(bloomTexture, texCoord);\n  vec4 fogColor   = texture(fogTexture,   texCoord);\n\n  fragColor     = baseColor;\n  fragColor     = fragColor + bloomColor;\n  fragColor     = mix(fragColor, fogColor, min(fogColor.a, 1));\n  fragColor     =\n    vec4\n      ( mix\n          ( backgroundColor.rgb\n          , fragColor.rgb\n          , min(baseColor.a + fogColor.a, 1)\n          )\n      , 1\n      );\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/screen-space-reflection.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform mat4 lensProjection;\n\nuniform sampler2D positionTexture;\nuniform sampler2D normalTexture;\nuniform sampler2D maskTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float maxDistance = 8;\n  float resolution  = 0.3;\n  int   steps       = 5;\n  float thickness   = 0.5;\n\n  vec2 texSize  = textureSize(positionTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv = vec4(0.0);\n\n  vec4 positionFrom = texture(positionTexture, texCoord);\n  vec4 mask         = texture(maskTexture,     texCoord);\n\n  if (  positionFrom.w <= 0.0\n     || enabled.x      != 1.0\n     || mask.r         <= 0.0\n     ) { fragColor = uv; return; }\n\n  vec3 unitPositionFrom = normalize(positionFrom.xyz);\n  vec3 normal           = normalize(texture(normalTexture, texCoord).xyz);\n  vec3 pivot            = normalize(reflect(unitPositionFrom, normal));\n\n  vec4 positionTo = positionFrom;\n\n  vec4 startView = vec4(positionFrom.xyz + (pivot *         0.0), 1.0);\n  vec4 endView   = vec4(positionFrom.xyz + (pivot * maxDistance), 1.0);\n\n  vec4 startFrag      = startView;\n       startFrag      = lensProjection * startFrag;\n       startFrag.xyz /= startFrag.w;\n       startFrag.xy   = startFrag.xy * 0.5 + 0.5;\n       startFrag.xy  *= texSize;\n\n  vec4 endFrag      = endView;\n       endFrag      = lensProjection * endFrag;\n       endFrag.xyz /= endFrag.w;\n       endFrag.xy   = endFrag.xy * 0.5 + 0.5;\n       endFrag.xy  *= texSize;\n\n  vec2 frag  = startFrag.xy;\n       uv.xy = frag / texSize;\n\n  float deltaX    = endFrag.x - startFrag.x;\n  float deltaY    = endFrag.y - startFrag.y;\n  float useX      = abs(deltaX) >= abs(deltaY) ? 1.0 : 0.0;\n  float delta     = mix(abs(deltaY), abs(deltaX), useX) * clamp(resolution, 0.0, 1.0);\n  vec2  increment = vec2(deltaX, deltaY) / max(delta, 0.001);\n\n  float search0 = 0;\n  float search1 = 0;\n\n  int hit0 = 0;\n  int hit1 = 0;\n\n  float viewDistance = startView.y;\n  float depth        = thickness;\n\n  float i = 0;\n\n  for (i = 0; i < int(delta); ++i) {\n    frag      += increment;\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionTexture, uv.xy);\n\n    search1 =\n      mix\n        ( (frag.y - startFrag.y) / deltaY\n        , (frag.x - startFrag.x) / deltaX\n        , useX\n        );\n\n    search1 = clamp(search1, 0.0, 1.0);\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n    depth        = viewDistance - positionTo.y;\n\n    if (depth > 0 && depth < thickness) {\n      hit0 = 1;\n      break;\n    } else {\n      search0 = search1;\n    }\n  }\n\n  search1 = search0 + ((search1 - search0) / 2.0);\n\n  steps *= hit0;\n\n  for (i = 0; i < steps; ++i) {\n    frag       = mix(startFrag.xy, endFrag.xy, search1);\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionTexture, uv.xy);\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n    depth        = viewDistance - positionTo.y;\n\n    if (depth > 0 && depth < thickness) {\n      hit1 = 1;\n      search1 = search0 + ((search1 - search0) / 2);\n    } else {\n      float temp = search1;\n      search1 = search1 + ((search1 - search0) / 2);\n      search0 = temp;\n    }\n  }\n\n  float visibility =\n      hit1\n    * positionTo.w\n    * ( 1\n      - max\n         ( dot(-unitPositionFrom, pivot)\n         , 0\n         )\n      )\n    * ( 1\n      - clamp\n          ( depth / thickness\n          , 0\n          , 1\n          )\n      )\n    * ( 1\n      - clamp\n          (   length(positionTo - positionFrom)\n            / maxDistance\n          , 0\n          , 1\n          )\n      )\n    * (uv.x < 0 || uv.x > 1 ? 0 : 1)\n    * (uv.y < 0 || uv.y > 1 ? 0 : 1);\n\n  visibility = clamp(visibility, 0, 1);\n\n  uv.ba = vec2(visibility);\n\n  fragColor = uv;\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/screen-space-refraction.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform mat4 lensProjection;\n\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\nuniform sampler2D normalFromTexture;\n\nuniform vec2 rior;\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float maxDistance = 5;\n  float resolution  = 0.3;\n  int   steps       = 5;\n  float thickness   = 0.5;\n\n  vec2 texSize  = textureSize(positionFromTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv = vec4(texCoord.xy, 1, 1);\n\n  vec4 positionFrom = texture(positionFromTexture, texCoord);\n\n  if (positionFrom.w <= 0 || enabled.x != 1) { fragColor = uv; return; }\n\n  vec3 unitPositionFrom = normalize(positionFrom.xyz);\n  vec3 normalFrom       = normalize(texture(normalFromTexture, texCoord).xyz);\n  vec3 pivot            = normalize(refract(unitPositionFrom, normalFrom, rior.x));\n\n  vec4 positionTo = positionFrom;\n\n  vec4 startView = vec4(positionFrom.xyz + (pivot *           0), 1);\n  vec4 endView   = vec4(positionFrom.xyz + (pivot * maxDistance), 1);\n\n  vec4 startFrag      = startView;\n       startFrag      = lensProjection * startFrag;\n       startFrag.xyz /= startFrag.w;\n       startFrag.xy   = startFrag.xy * 0.5 + 0.5;\n       startFrag.xy  *= texSize;\n\n  vec4 endFrag      = endView;\n       endFrag      = lensProjection * endFrag;\n       endFrag.xyz /= endFrag.w;\n       endFrag.xy   = endFrag.xy * 0.5 + 0.5;\n       endFrag.xy  *= texSize;\n\n  vec2 frag  = startFrag.xy;\n       uv.xy = frag / texSize;\n\n  float deltaX    = endFrag.x - startFrag.x;\n  float deltaY    = endFrag.y - startFrag.y;\n  float useX      = abs(deltaX) >= abs(deltaY) ? 1 : 0;\n  float delta     = mix(abs(deltaY), abs(deltaX), useX) * clamp(resolution, 0, 1);\n  vec2  increment = vec2(deltaX, deltaY) / max(delta, 0.001);\n\n  float search0 = 0;\n  float search1 = 0;\n\n  int hit0 = 0;\n  int hit1 = 0;\n\n  float viewDistance = startView.y;\n  float depth        = thickness;\n\n  float i = 0;\n\n  for (i = 0; i < int(delta); ++i) {\n    frag      += increment;\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionToTexture, uv.xy);\n\n    search1 =\n      mix\n        ( (frag.y - startFrag.y) / deltaY\n        , (frag.x - startFrag.x) / deltaX\n        , useX\n        );\n\n    search1 = clamp(search1, 0, 1);\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n    depth        = viewDistance - positionTo.y;\n\n    if (depth > 0 && depth < thickness) {\n      hit0 = 1;\n      break;\n    } else {\n      search0 = search1;\n    }\n  }\n\n  search1 = search0 + ((search1 - search0) / 2);\n\n  steps *= hit0;\n\n  for (i = 0; i < steps; ++i) {\n    frag       = mix(startFrag.xy, endFrag.xy, search1);\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionToTexture, uv.xy);\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n    depth        = viewDistance - positionTo.y;\n\n    if (depth > 0 && depth < thickness) {\n      hit1 = 1;\n      search1 = search0 + ((search1 - search0) / 2);\n    } else {\n      float temp = search1;\n      search1 = search1 + ((search1 - search0) / 2);\n      search0 = temp;\n    }\n  }\n\n  float visibility =\n      hit1\n    * positionTo.w\n    * ( 1\n      - max\n         ( dot(-unitPositionFrom, pivot)\n         , 0\n         )\n      )\n    * (uv.x < 0 || uv.x > 1 ? 0 : 1)\n    * (uv.y < 0 || uv.y > 1 ? 0 : 1);\n\n  visibility = clamp(visibility, 0, 1);\n\n  fragColor = vec4(mix(texCoord.xy, uv.xy, visibility), 1, 1);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/sharpen.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform sampler2D colorTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float amount = 0.3;\n\n  vec2 texSize   = textureSize(colorTexture, 0).xy;\n  vec2 fragCoord = gl_FragCoord.xy;\n  vec2 texCoord  = fragCoord / texSize;\n\n  if (enabled.x != 1) { fragColor = texture(colorTexture, texCoord); return; }\n\n  float neighbor = amount * -1.0;\n  float center   = amount *  4.0 + 1.0;\n\n  vec3 color =\n        texture(colorTexture, (fragCoord + vec2( 0,  1)) / texSize).rgb\n      * neighbor\n\n      + texture(colorTexture, (fragCoord + vec2(-1,  0)) / texSize).rgb\n      * neighbor\n      + texture(colorTexture, (fragCoord + vec2( 0,  0)) / texSize).rgb\n      * center\n      + texture(colorTexture, (fragCoord + vec2( 1,  0)) / texSize).rgb\n      * neighbor\n\n      + texture(colorTexture, (fragCoord + vec2( 0, -1)) / texSize).rgb\n      * neighbor\n      ;\n\n  fragColor = vec4(color, texture(colorTexture, texCoord).a);\n}\n"
  },
  {
    "path": "demonstration/shaders/fragment/ssao.frag",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define NUM_SAMPLES 8\n#define NUM_NOISE   4\n\nuniform mat4 lensProjection;\n\nuniform vec3 samples[NUM_SAMPLES];\nuniform vec3 noise[NUM_NOISE];\n\nuniform sampler2D positionTexture;\nuniform sampler2D normalTexture;\n\nuniform vec2 enabled;\n\nout vec4 fragColor;\n\nvoid main() {\n  float radius    = 0.6;\n  float bias      = 0.005;\n  float magnitude = 1.1;\n  float contrast  = 1.1;\n\n  fragColor = vec4(1);\n\n  if (enabled.x != 1) { return; }\n\n  vec2 texSize  = textureSize(positionTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 position = texture(positionTexture, texCoord);\n  if (position.a <= 0) { return; }\n\n  vec3 normal = normalize(texture(normalTexture, texCoord).xyz);\n\n  int  noiseS = int(sqrt(NUM_NOISE));\n  int  noiseX = int(gl_FragCoord.x - 0.5) % noiseS;\n  int  noiseY = int(gl_FragCoord.y - 0.5) % noiseS;\n  vec3 random = noise[noiseX + (noiseY * noiseS)];\n\n  vec3 tangent  = normalize(random - normal * dot(random, normal));\n  vec3 binormal = cross(normal, tangent);\n  mat3 tbn      = mat3(tangent, binormal, normal);\n\n  float occlusion = NUM_SAMPLES;\n\n  for (int i = 0; i < NUM_SAMPLES; ++i) {\n    vec3 samplePosition = tbn * samples[i];\n         samplePosition = position.xyz + samplePosition * radius;\n\n    vec4 offsetUV      = vec4(samplePosition, 1.0);\n         offsetUV      = lensProjection * offsetUV;\n         offsetUV.xyz /= offsetUV.w;\n         offsetUV.xy   = offsetUV.xy * 0.5 + 0.5;\n\n    // Config.prc\n    // gl-coordinate-system  default\n    // textures-auto-power-2 1\n    // textures-power-2      down\n\n    vec4 offsetPosition = texture(positionTexture, offsetUV.xy);\n\n    float occluded = 0;\n    if   (samplePosition.y + bias <= offsetPosition.y)\n         { occluded = 0; }\n    else { occluded = 1; }\n\n    float intensity =\n      smoothstep\n        ( 0\n        , 1\n        ,   radius\n          / abs(position.y - offsetPosition.y)\n        );\n    occluded  *= intensity;\n    occlusion -= occluded;\n  }\n\n  occlusion /= NUM_SAMPLES;\n  occlusion  = pow(occlusion, magnitude);\n  occlusion  = contrast * (occlusion - 0.5) + 0.5;\n\n  fragColor = vec4(vec3(occlusion), position.a);\n}\n"
  },
  {
    "path": "demonstration/shaders/vertex/base.vert",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\n#define NUMBER_OF_LIGHTS 4\n\nuniform mat4 p3d_ModelViewMatrix;\nuniform mat4 p3d_ProjectionMatrix;\nuniform mat3 p3d_NormalMatrix;\n\nuniform struct p3d_LightSourceParameters\n  { vec4 color\n\n  ; vec4 ambient\n  ; vec4 diffuse\n  ; vec4 specular\n\n  ; vec4 position\n\n  ; vec3  spotDirection\n  ; float spotExponent\n  ; float spotCutoff\n  ; float spotCosCutoff\n\n  ; float constantAttenuation\n  ; float linearAttenuation\n  ; float quadraticAttenuation\n\n  ; vec3 attenuation\n\n  ; sampler2DShadow shadowMap\n\n  ; mat4 shadowViewMatrix\n  ;\n  } p3d_LightSource[NUMBER_OF_LIGHTS];\n\nin vec4 p3d_Vertex;\nin vec3 p3d_Normal;\n\nin vec4 p3d_Color;\n\nin vec2 p3d_MultiTexCoord0;\nin vec2 p3d_MultiTexCoord1;\n\nin vec3 p3d_Binormal;\nin vec3 p3d_Tangent;\n\nout vec4 vertexPosition;\nout vec4 vertexColor;\n\nout vec3 vertexNormal;\nout vec3 binormal;\nout vec3 tangent;\n\nout vec2 normalCoord;\nout vec2 diffuseCoord;\n\nout vec4 vertexInShadowSpaces[NUMBER_OF_LIGHTS];\n\nvoid main() {\n  vertexColor    = p3d_Color;\n  vertexPosition = p3d_ModelViewMatrix * p3d_Vertex;\n\n  vertexNormal = normalize(p3d_NormalMatrix * p3d_Normal);\n  binormal     = normalize(p3d_NormalMatrix * p3d_Binormal);\n  tangent      = normalize(p3d_NormalMatrix * p3d_Tangent);\n\n  normalCoord   = p3d_MultiTexCoord0;\n  diffuseCoord  = p3d_MultiTexCoord1;\n\n  for (int i = 0; i < p3d_LightSource.length(); ++i) {\n    vertexInShadowSpaces[i] = p3d_LightSource[i].shadowViewMatrix * vertexPosition;\n  }\n\n  gl_Position = p3d_ProjectionMatrix * vertexPosition;\n}\n"
  },
  {
    "path": "demonstration/shaders/vertex/basic.vert",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nuniform mat4 p3d_ModelViewProjectionMatrix;\n\nin vec4 p3d_Vertex;\n\nvoid main()\n{\n  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n}\n"
  },
  {
    "path": "demonstration/shaders/vertex/discard.vert",
    "content": "/*\n  (C) 2020 David Lettier\n  lettier.com\n*/\n\n#version 150\n\nvoid main()\n{\n  gl_Position = vec4(vec3(2.0), 1.0);\n}\n"
  },
  {
    "path": "demonstration/src/LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2019, David Lettier\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "demonstration/src/main.cxx",
    "content": "/*\n  (C) 2019 David Lettier\n  lettier.com\n*/\n\n#include <thread>\n#include <unistd.h>\n#include <random>\n#include <string>\n#include <chrono>\n#include <thread>\n#include <algorithm>\n\n#include \"pandaFramework.h\" // Panda3D 1.10.9\n#include \"renderBuffer.h\"\n#include \"load_prc_file.h\"\n#include \"pStatClient.h\"\n#include \"pandaSystem.h\"\n#include \"mouseButton.h\"\n#include \"mouseWatcher.h\"\n#include \"buttonRegistry.h\"\n#include \"orthographicLens.h\"\n#include \"ambientLight.h\"\n#include \"directionalLight.h\"\n#include \"pointLight.h\"\n#include \"spotlight.h\"\n#include \"shader.h\"\n#include \"nodePathCollection.h\"\n#include \"auto_bind.h\"\n#include \"animControlCollection.h\"\n#include \"cardMaker.h\"\n#include \"fontPool.h\"\n#include \"texturePool.h\"\n#include \"particleSystemManager.h\"\n#include \"physicsManager.h\"\n#include \"spriteParticleRenderer.h\"\n#include \"pointParticleFactory.h\"\n#include \"pointEmitter.h\"\n#include \"physicalNode.h\"\n#include \"forceNode.h\"\n#include \"linearNoiseForce.h\"\n#include \"linearVectorForce.h\"\n#include \"linearJitterForce.h\"\n#include \"linearCylinderVortexForce.h\"\n#include \"linearEulerIntegrator.h\"\n#include \"audioManager.h\"\n#include \"audioSound.h\"\n\n// STRUCTURES\n\nstruct FramebufferTexture\n  { PT(GraphicsOutput) buffer\n  ; PT(DisplayRegion) bufferRegion\n  ; PT(Camera) camera\n  ; NodePath cameraNP\n  ; NodePath shaderNP\n  ;\n  };\n\nstruct FramebufferTextureArguments\n  { PT(WindowFramework) window\n  ; PT(GraphicsOutput) graphicsOutput\n  ; PT(GraphicsEngine) graphicsEngine\n  ; GraphicsOutput::RenderTexturePlane bitplane\n  ; LVecBase4 rgbaBits\n  ; LColor clearColor\n  ; int aux_rgba\n  ; bool setFloatColor\n  ; bool setSrgbColor\n  ; bool setRgbColor\n  ; bool useScene\n  ; std::string name\n  ;\n  };\n\n// END STRUCTURES\n\n// FUNCTIONS\n\nvoid generateLights\n  ( NodePath render\n  , bool showLights\n  );\nvoid generateWindowLight\n  ( std::string name\n  , NodePath render\n  , LVecBase3 position\n  , bool show\n  );\nfloat animateLights\n  ( NodePath render\n  , AnimControlCollection shuttersAnimationCollection\n  , float delta\n  , float speed\n  , bool& closedShutters\n  , bool  middayDown\n  , bool  midnightDown\n  );\n\nPT(Shader) loadShader\n  ( std::string vert\n  , std::string frag\n  );\n\nFramebufferTexture generateFramebufferTexture\n  ( FramebufferTextureArguments framebufferTextureArguments\n  );\n\nPTA_LVecBase3f generateSsaoSamples\n  ( int numberOfSamples\n  );\nPTA_LVecBase3f generateSsaoNoise\n  ( int numberOfNoise\n  );\n\nvoid showBuffer\n  ( NodePath render2d\n  , NodePath statusNP\n  , std::tuple<std::string, PT(GraphicsOutput), int> bufferTexture\n  , bool alpha\n  );\n\nvoid hideBuffer\n  ( NodePath render2d\n  );\n\nint microsecondsSinceEpoch\n  (\n  );\n\nbool isButtonDown\n  ( PT(MouseWatcher) mouseWatcher\n  , std::string character\n  );\n\nPT(MouseWatcher) getMouseWatcher\n  ( WindowFramework* window\n  );\n\nvoid setSoundOff\n  ( PT(AudioSound) sound\n  );\n\nvoid setSoundOn\n  ( PT(AudioSound) sound\n  );\n\nvoid setSoundState\n  ( PT(AudioSound) sound\n  , bool on\n  );\n\nvoid updateAudoManager\n  ( NodePath sceneRootNP\n  , NodePath cameraNP\n  );\n\nLVecBase3f calculateCameraPosition\n  ( double radius\n  , double phi\n  , double theta\n  , LVecBase3 cameraLookAt\n  );\n\nLVecBase3f calculateCameraLookAt\n  ( double upDownAdjust\n  , double leftRightAdjust\n  , double phi\n  , double theta\n  , LVecBase3 lookAt\n  );\n\nNodePath setUpParticles\n  ( NodePath render\n  , PT(Texture) smokeTexture\n  );\n\nvoid squashGeometry\n  ( NodePath environmentNP\n  );\n\ndouble microsecondToSecond\n  ( int m\n  );\n\ndouble toRadians\n  ( double d\n  );\n\nLVecBase2f makeEnabledVec\n  ( int t\n  );\n\nLVecBase2f toggleEnabledVec\n  ( LVecBase2f vec\n  );\n\nvoid setTextureToNearestAndClamp\n  ( PT(Texture) texture\n  );\n\nLColor mixColor\n  ( LColor a\n  , LColor b\n  , float amount\n  );\n\n// END FUNCTIONS\n\n// GLOBALS\n\nconst float TO_RAD               = M_PI / 180.0;\nconst LVecBase2f PI_SHADER_INPUT = LVecBase2f(M_PI, TO_RAD);\n\nconst float GAMMA                   = 2.2;\nconst float GAMMA_REC               = 1.0 / GAMMA;\nconst LVecBase2f GAMMA_SHADER_INPUT = LVecBase2f(GAMMA, GAMMA_REC);\n\nconst int BACKGROUND_RENDER_SORT_ORDER = 10;\nconst int UNSORTED_RENDER_SORT_ORDER   = 50;\n\nconst int SSAO_SAMPLES = 8;\nconst int SSAO_NOISE   = 4;\n\nconst int SHADOW_SIZE = 2048;\n\nLVecBase4f sunlightColor0 =\n  LVecBase4f\n    ( 0.612\n    , 0.365\n    , 0.306\n    , 1\n    );\nLVecBase4f sunlightColor1 =\n  LVecBase4f\n    ( 0.765\n    , 0.573\n    , 0.400\n    , 1\n    );\nLVecBase4f moonlightColor0 =\n  LVecBase4f\n    ( 0.247\n    , 0.384\n    , 0.404\n    , 1\n    );\nLVecBase4f moonlightColor1 =\n  LVecBase4f\n    ( 0.392\n    , 0.537\n    , 0.571\n    , 1\n    );\nLVecBase4f windowLightColor =\n  LVecBase4f\n    ( 0.765\n    , 0.573\n    , 0.400\n    , 1\n    );\n\nstd::uniform_real_distribution<float>\n  randomFloats\n    ( 0.0\n    , 1.0\n    );\nstd::default_random_engine generator;\n\nPT(AsyncTaskManager) taskManager = AsyncTaskManager::get_global_ptr();\n\nPT(AudioManager) audioManager = AudioManager::create_AudioManager();\n\nParticleSystemManager particleSystemManager = ParticleSystemManager();\n\nPhysicsManager physicsManager = PhysicsManager();\n\n// END GLOBALS\n\n// MAIN\n\nint main\n  ( int argc\n  , char *argv[]\n  ) {\n  LColor backgroundColor [] =\n    { LColor\n        ( 0.392\n        , 0.537\n        , 0.561\n        , 1\n        )\n    , LColor\n        ( 0.953\n        , 0.733\n        , 0.525\n        , 1\n        )\n    };\n\n  double     cameraRotatePhiInitial    =   67.5095;\n  double     cameraRotateThetaInitial  =  231.721;\n  double     cameraRotateRadiusInitial = 1100.83;\n  LVecBase3  cameraLookAtInitial       = LVecBase3(1.00839, 1.20764, 5.85055);\n  float      cameraFov                 =    1.0;\n  int        cameraNear                =  150;\n  int        cameraFar                 = 2000;\n  LVecBase2f cameraNearFar             = LVecBase2f(cameraNear, cameraFar);\n  double     cameraRotateRadius        = cameraRotateRadiusInitial;\n  double     cameraRotatePhi           = cameraRotatePhiInitial;\n  double     cameraRotateTheta         = cameraRotateThetaInitial;\n  LVecBase3  cameraLookAt              = cameraLookAtInitial;\n\n  float fogNearInitial = 2.0;\n  float fogFarInitial  = 9.0;\n  float fogNear        = fogNearInitial;\n  float fogFar         = fogFarInitial;\n  float fogAdjust      = 0.1;\n\n  LVecBase2f foamDepthInitial = LVecBase2f(1.5, 1.5);\n  float      foamDepthAdjust  = 0.1;\n  LVecBase2f foamDepth        = foamDepthInitial;\n\n  LVecBase2f mouseThen = LVecBase2f(0.0, 0.0);\n  LVecBase2f mouseNow  = mouseThen;\n  bool mouseWheelDown  = false;\n  bool mouseWheelUp    = false;\n\n  LVecBase2f riorInitial = LVecBase2f(1.05, 1.05);\n  float      riorAdjust  = 0.005;\n  LVecBase2f rior        = riorInitial;\n\n  LVecBase2f mouseFocusPointInitial = LVecBase2f(0.509167, 0.598);\n  LVecBase2f mouseFocusPoint        = mouseFocusPointInitial;\n\n  float sunlightP       = 260;\n  bool  animateSunlight = true;\n\n  bool  soundEnabled  = true;\n  bool  soundStarted  = false;\n  float startSoundAt  = 0.5;\n\n  bool closedShutters  = true;\n\n  float       statusAlpha       = 1.0;\n  LColor      statusColor       = LColor(0.9, 0.9, 1.0, statusAlpha);\n  LColor      statusShadowColor = LColor(0.1, 0.1, 0.3, statusAlpha);\n  float       statusFadeRate    = 2.0;\n  std::string statusText        = \"Ready\";\n\n  LVecBase2f ssaoEnabled                = makeEnabledVec(1);\n  LVecBase2f blinnPhongEnabled          = makeEnabledVec(1);\n  LVecBase2f fresnelEnabled             = makeEnabledVec(1);\n  LVecBase2f rimLightEnabled            = makeEnabledVec(1);\n  LVecBase2f refractionEnabled          = makeEnabledVec(1);\n  LVecBase2f reflectionEnabled          = makeEnabledVec(1);\n  LVecBase2f fogEnabled                 = makeEnabledVec(1);\n  LVecBase2f outlineEnabled             = makeEnabledVec(1);\n  LVecBase2f celShadingEnabled          = makeEnabledVec(1);\n  LVecBase2f normalMapsEnabled          = makeEnabledVec(1);\n  LVecBase2f bloomEnabled               = makeEnabledVec(1);\n  LVecBase2f sharpenEnabled             = makeEnabledVec(1);\n  LVecBase2f depthOfFieldEnabled        = makeEnabledVec(1);\n  LVecBase2f filmGrainEnabled           = makeEnabledVec(1);\n  LVecBase2f flowMapsEnabled            = makeEnabledVec(1);\n  LVecBase2f lookupTableEnabled         = makeEnabledVec(1);\n  LVecBase2f painterlyEnabled           = makeEnabledVec(0);\n  LVecBase2f motionBlurEnabled          = makeEnabledVec(0);\n  LVecBase2f posterizeEnabled           = makeEnabledVec(0);\n  LVecBase2f pixelizeEnabled            = makeEnabledVec(0);\n  LVecBase2f chromaticAberrationEnabled = makeEnabledVec(1);\n\n  LVecBase4 rgba8  = ( 8,  8,  8,  8);\n  LVecBase4 rgba16 = (16, 16, 16, 16);\n  LVecBase4 rgba32 = (32, 32, 32, 32);\n\n  load_prc_file(\"panda3d-prc-file.prc\");\n\n  PT(TextFont) font = FontPool::load_font(\"fonts/font.ttf\");\n\n  std::vector<PT(AudioSound)> sounds =\n    { audioManager->get_sound(\"sounds/wheel.ogg\", true)\n    , audioManager->get_sound(\"sounds/water.ogg\", true)\n    };\n\n  PT(Texture) blankTexture             = TexturePool::load_texture(\"images/blank.png\");\n  PT(Texture) foamPatternTexture       = TexturePool::load_texture(\"images/foam-pattern.png\");\n  PT(Texture) stillFlowTexture         = TexturePool::load_texture(\"images/still-flow.png\");\n  PT(Texture) upFlowTexture            = TexturePool::load_texture(\"images/up-flow.png\");\n  PT(Texture) colorLookupTableTextureN = TexturePool::load_texture(\"images/lookup-table-neutral.png\");\n  PT(Texture) colorLookupTableTexture0 = TexturePool::load_texture(\"images/lookup-table-0.png\");\n  PT(Texture) colorLookupTableTexture1 = TexturePool::load_texture(\"images/lookup-table-1.png\");\n  PT(Texture) smokeTexture             = TexturePool::load_texture(\"images/smoke.png\");\n  PT(Texture) colorNoiseTexture        = TexturePool::load_texture(\"images/color-noise.png\");\n\n  setTextureToNearestAndClamp(colorLookupTableTextureN);\n  setTextureToNearestAndClamp(colorLookupTableTexture0);\n  setTextureToNearestAndClamp(colorLookupTableTexture1);\n\n  PandaFramework framework;\n  framework.open_framework(argc, argv);\n  framework.set_window_title(\"3D Game Shaders For Beginners By David Lettier\");\n\n  PT(WindowFramework)         window                = framework.open_window();\n  PT(GraphicsWindow)          graphicsWindow        = window->get_graphics_window();\n  PT(GraphicsOutput)          graphicsOutput        = window->get_graphics_output();\n  PT(GraphicsStateGuardian)   graphicsStateGuardian = graphicsOutput->get_gsg();\n  PT(GraphicsEngine)          graphicsEngine        = graphicsStateGuardian->get_engine();\n\n  window->enable_keyboard();\n\n  PT(DisplayRegion) displayRegion3d = window->get_display_region_3d();\n  displayRegion3d->set_clear_color_active(true);\n  displayRegion3d->set_clear_depth_active(true);\n  displayRegion3d->set_clear_stencil_active(true);\n  displayRegion3d->set_clear_color(backgroundColor[1]);\n  displayRegion3d->set_clear_depth(1.0f);\n  displayRegion3d->set_clear_stencil(0);\n\n  NodePath render   = window->get_render();\n  NodePath render2d = window->get_render_2d();\n\n  PT(TextNode) status = new TextNode(\"status\");\n  status->set_font(font);\n  status->set_text(statusText);\n  status->set_text_color(statusColor);\n  status->set_shadow(0.0, 0.06);\n  status->set_shadow_color(statusShadowColor);\n  NodePath statusNP = render2d.attach_new_node(status);\n  statusNP.set_scale(0.05);\n  statusNP.set_pos(-0.96, 0, -0.95);\n\n  PT(MouseWatcher) mouseWatcher = getMouseWatcher(window);\n\n  PT(Camera) mainCamera = window->get_camera(0);\n  PT(Lens) mainLens = mainCamera->get_lens();\n  mainLens->set_fov(cameraFov);\n  mainLens->set_near_far(cameraNear, cameraFar);\n\n  NodePath cameraNP = window->get_camera_group();\n\n  cameraNP.set_pos\n    ( calculateCameraPosition\n        ( cameraRotateRadius\n        , cameraRotatePhi\n        , cameraRotateTheta\n        , cameraLookAt\n        )\n    );\n  cameraNP.look_at(cameraLookAt);\n\n  PT(PandaNode) sceneRootPN = new PandaNode(\"sceneRoot\");\n  NodePath sceneRootNP      = NodePath(sceneRootPN);\n  sceneRootNP.reparent_to(render);\n\n  NodePath environmentNP =\n    window\n      ->load_model\n        ( framework.get_models()\n        , \"eggs/mill-scene/mill-scene.bam\"\n        );\n  environmentNP.reparent_to(sceneRootNP);\n  NodePath shuttersNP =\n    window\n      ->load_model\n        ( framework.get_models()\n        , \"eggs/mill-scene/shutters.bam\"\n        );\n  shuttersNP.reparent_to(sceneRootNP);\n  NodePath weatherVaneNP =\n    window\n      ->load_model\n        ( framework.get_models()\n        , \"eggs/mill-scene/weather-vane.bam\"\n        );\n  weatherVaneNP.reparent_to(sceneRootNP);\n  NodePath bannerNP =\n    window\n      ->load_model\n        ( framework.get_models()\n        , \"eggs/mill-scene/banner.bam\"\n        );\n  bannerNP.reparent_to(sceneRootNP);\n\n  NodePath wheelNP   = environmentNP.find(\"**/wheel-lp\");\n  NodePath waterNP   = environmentNP.find(\"**/water-lp\");\n\n  squashGeometry(environmentNP);\n\n  NodePath smokeNP = setUpParticles(render, smokeTexture);\n\n  waterNP.set_transparency(TransparencyAttrib::M_dual);\n  waterNP.set_bin(\"fixed\", 0);\n\n  AnimControlCollection shuttersAnimationCollection;\n  AnimControlCollection weatherVaneAnimationCollection;\n  AnimControlCollection bannerAnimationCollection;\n  auto_bind\n    ( shuttersNP.node()\n    , shuttersAnimationCollection\n    ,   PartGroup::HMF_ok_wrong_root_name\n      | PartGroup::HMF_ok_part_extra\n      | PartGroup::HMF_ok_anim_extra\n    );\n  auto_bind\n    ( weatherVaneNP.node()\n    , weatherVaneAnimationCollection\n    ,   PartGroup::HMF_ok_wrong_root_name\n      | PartGroup::HMF_ok_part_extra\n      | PartGroup::HMF_ok_anim_extra\n    );\n  auto_bind\n    ( bannerNP.node()\n    , bannerAnimationCollection\n    ,   PartGroup::HMF_ok_wrong_root_name\n      | PartGroup::HMF_ok_part_extra\n      | PartGroup::HMF_ok_anim_extra\n    );\n\n  generateLights(render, false);\n\n  PT(Shader) discardShader               = loadShader(\"discard\", \"discard\");\n  PT(Shader) baseShader                  = loadShader(\"base\",    \"base\");\n  PT(Shader) geometryBufferShader0       = loadShader(\"base\",    \"geometry-buffer-0\");\n  PT(Shader) geometryBufferShader1       = loadShader(\"base\",    \"geometry-buffer-1\");\n  PT(Shader) geometryBufferShader2       = loadShader(\"base\",    \"geometry-buffer-2\");\n  PT(Shader) foamShader                  = loadShader(\"basic\",   \"foam\");\n  PT(Shader) fogShader                   = loadShader(\"basic\",   \"fog\");\n  PT(Shader) boxBlurShader               = loadShader(\"basic\",   \"box-blur\");\n  PT(Shader) motionBlurShader            = loadShader(\"basic\",   \"motion-blur\");\n  PT(Shader) kuwaharaFilterShader        = loadShader(\"basic\",   \"kuwahara-filter\");\n  PT(Shader) dilationShader              = loadShader(\"basic\",   \"dilation\");\n  PT(Shader) sharpenShader               = loadShader(\"basic\",   \"sharpen\");\n  PT(Shader) outlineShader               = loadShader(\"basic\",   \"outline\");\n  PT(Shader) bloomShader                 = loadShader(\"basic\",   \"bloom\");\n  PT(Shader) ssaoShader                  = loadShader(\"basic\",   \"ssao\");\n  PT(Shader) screenSpaceRefractionShader = loadShader(\"basic\",   \"screen-space-refraction\");\n  PT(Shader) screenSpaceReflectionShader = loadShader(\"basic\",   \"screen-space-reflection\");\n  PT(Shader) refractionShader            = loadShader(\"basic\",   \"refraction\");\n  PT(Shader) reflectionColorShader       = loadShader(\"basic\",   \"reflection-color\");\n  PT(Shader) reflectionShader            = loadShader(\"basic\",   \"reflection\");\n  PT(Shader) baseCombineShader           = loadShader(\"basic\",   \"base-combine\");\n  PT(Shader) sceneCombineShader          = loadShader(\"basic\",   \"scene-combine\");\n  PT(Shader) depthOfFieldShader          = loadShader(\"basic\",   \"depth-of-field\");\n  PT(Shader) posterizeShader             = loadShader(\"basic\",   \"posterize\");\n  PT(Shader) pixelizeShader              = loadShader(\"basic\",   \"pixelize\");\n  PT(Shader) filmGrainShader             = loadShader(\"basic\",   \"film-grain\");\n  PT(Shader) lookupTableShader           = loadShader(\"basic\",   \"lookup-table\");\n  PT(Shader) gammaCorrectionShader       = loadShader(\"basic\",   \"gamma-correction\");\n  PT(Shader) chromaticAberrationShader   = loadShader(\"basic\",   \"chromatic-aberration\");\n\n  NodePath mainCameraNP = NodePath(\"mainCamera\");\n  mainCameraNP.set_shader(discardShader);\n  mainCamera->set_initial_state(mainCameraNP.get_state());\n\n  NodePath isWaterNP = NodePath(\"isWater\");\n  isWaterNP.set_shader_input(\"isWater\",            LVecBase2f(1.0, 1.0));\n  isWaterNP.set_shader_input(\"flowTexture\",        upFlowTexture);\n  isWaterNP.set_shader_input(\"foamPatternTexture\", foamPatternTexture);\n\n  NodePath isSmokeNP = NodePath(\"isSmoke\");\n  isSmokeNP.set_shader_input(\"isSmoke\",    LVecBase2f(1.0, 1.0));\n  isSmokeNP.set_shader_input(\"isParticle\", LVecBase2f(1.0, 1.0));\n\n  LMatrix4 currentViewWorldMat      = cameraNP.get_transform(render)->get_mat();\n  LMatrix4 previousViewWorldMat     = previousViewWorldMat;\n\n  FramebufferTextureArguments framebufferTextureArguments;\n  framebufferTextureArguments.window         = window;\n  framebufferTextureArguments.graphicsOutput = graphicsOutput;\n  framebufferTextureArguments.graphicsEngine = graphicsEngine;\n\n  framebufferTextureArguments.bitplane       = GraphicsOutput::RTP_color;\n  framebufferTextureArguments.rgbaBits       = rgba32;\n  framebufferTextureArguments.clearColor     = LColor(0, 0, 0, 0);\n  framebufferTextureArguments.aux_rgba       = 1;\n  framebufferTextureArguments.setFloatColor  = true;\n  framebufferTextureArguments.setSrgbColor   = false;\n  framebufferTextureArguments.setRgbColor    = true;\n  framebufferTextureArguments.useScene       = true;\n  framebufferTextureArguments.name           = \"geometry0\";\n\n  FramebufferTexture geometryFramebufferTexture0 =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) geometryBuffer0 = geometryFramebufferTexture0.buffer;\n  PT(Camera)         geometryCamera0 = geometryFramebufferTexture0.camera;\n  NodePath           geometryNP0     = geometryFramebufferTexture0.shaderNP;\n  geometryBuffer0->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_0\n    );\n  geometryBuffer0->set_clear_active(3, true);\n  geometryBuffer0->set_clear_value( 3, framebufferTextureArguments.clearColor);\n  geometryNP0.set_shader(geometryBufferShader0);\n  geometryNP0.set_shader_input(\"normalMapsEnabled\", normalMapsEnabled);\n  geometryCamera0->set_initial_state(geometryNP0.get_state());\n  geometryCamera0->set_camera_mask(BitMask32::bit(1));\n  PT(Texture) positionTexture0    = geometryBuffer0->get_texture(0);\n  PT(Texture) normalTexture0      = geometryBuffer0->get_texture(1);\n  PT(Lens)    geometryCameraLens0 = geometryCamera0->get_lens();\n  waterNP.hide(BitMask32::bit(1));\n  smokeNP.hide(BitMask32::bit(1));\n\n  framebufferTextureArguments.aux_rgba = 4;\n  framebufferTextureArguments.name     = \"geometry1\";\n\n  FramebufferTexture geometryFramebufferTexture1 =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) geometryBuffer1 = geometryFramebufferTexture1.buffer;\n  PT(Camera)         geometryCamera1 = geometryFramebufferTexture1.camera;\n  NodePath           geometryNP1     = geometryFramebufferTexture1.shaderNP;\n  geometryBuffer1->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_0\n    );\n  geometryBuffer1->set_clear_active(3, true);\n  geometryBuffer1->set_clear_value( 3, framebufferTextureArguments.clearColor);\n  geometryBuffer1->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_1\n    );\n  geometryBuffer1->set_clear_active(4, true);\n  geometryBuffer1->set_clear_value( 4, framebufferTextureArguments.clearColor);\n  geometryBuffer1->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_2\n    );\n  geometryBuffer1->set_clear_active(5, true);\n  geometryBuffer1->set_clear_value( 5, framebufferTextureArguments.clearColor);\n  geometryBuffer1->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_3\n    );\n  geometryBuffer1->set_clear_active(6, true);\n  geometryBuffer1->set_clear_value( 6, framebufferTextureArguments.clearColor);\n  geometryNP1.set_shader(geometryBufferShader1);\n  geometryNP1.set_shader_input(\"normalMapsEnabled\",  normalMapsEnabled);\n  geometryNP1.set_shader_input(\"flowTexture\",        stillFlowTexture);\n  geometryNP1.set_shader_input(\"foamPatternTexture\", blankTexture);\n  geometryNP1.set_shader_input(\"flowMapsEnabled\",    flowMapsEnabled);\n  geometryCamera1->set_initial_state(geometryNP1.get_state());\n  geometryCamera1->set_tag_state_key(\"geometryBuffer1\");\n  geometryCamera1->set_tag_state(\"isWater\", isWaterNP.get_state());\n  geometryCamera1->set_camera_mask(BitMask32::bit(2));\n  PT(Texture) positionTexture1        = geometryBuffer1->get_texture(0);\n  PT(Texture) normalTexture1          = geometryBuffer1->get_texture(1);\n  PT(Texture) reflectionMaskTexture   = geometryBuffer1->get_texture(2);\n  PT(Texture) refractionMaskTexture   = geometryBuffer1->get_texture(3);\n  PT(Texture) foamMaskTexture         = geometryBuffer1->get_texture(4);\n  PT(Lens)    geometryCameraLens1     = geometryCamera1->get_lens();\n  waterNP.set_tag(\"geometryBuffer1\", \"isWater\");\n  smokeNP.hide(BitMask32::bit(2));\n\n  framebufferTextureArguments.aux_rgba = 1;\n  framebufferTextureArguments.name     = \"geometry2\";\n\n  FramebufferTexture geometryFramebufferTexture2 =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) geometryBuffer2 = geometryFramebufferTexture2.buffer;\n  PT(Camera)         geometryCamera2 = geometryFramebufferTexture2.camera;\n  NodePath           geometryNP2     = geometryFramebufferTexture2.shaderNP;\n  geometryBuffer2->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_0\n    );\n  geometryBuffer2->set_clear_active(3, true);\n  geometryBuffer2->set_clear_value( 3, framebufferTextureArguments.clearColor);\n  geometryBuffer2->set_sort(geometryBuffer1->get_sort() + 1);\n  geometryNP2.set_shader(geometryBufferShader2);\n  geometryNP2.set_shader_input(\"isSmoke\",         LVecBase2f(0, 0));\n  geometryNP2.set_shader_input(\"positionTexture\", positionTexture1);\n  geometryCamera2->set_initial_state(geometryNP2.get_state());\n  geometryCamera2->set_tag_state_key(\"geometryBuffer2\");\n  geometryCamera2->set_tag_state(\"isSmoke\", isSmokeNP.get_state());\n  smokeNP.set_tag(\"geometryBuffer2\", \"isSmoke\");\n  PT(Texture) positionTexture2         = geometryBuffer2->get_texture(0);\n  PT(Texture) smokeMaskTexture         = geometryBuffer2->get_texture(1);\n  PT(Lens)    geometryCameraLens2      = geometryCamera2->get_lens();\n\n  framebufferTextureArguments.rgbaBits      = rgba8;\n  framebufferTextureArguments.aux_rgba      = 0;\n  framebufferTextureArguments.clearColor    = LColor(0, 0, 0, 0);\n  framebufferTextureArguments.setFloatColor = false;\n  framebufferTextureArguments.useScene      = false;\n  framebufferTextureArguments.name          = \"fog\";\n\n  FramebufferTexture fogFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) fogBuffer = fogFramebufferTexture.buffer;\n  PT(Camera)         fogCamera = fogFramebufferTexture.camera;\n  NodePath           fogNP     = fogFramebufferTexture.shaderNP;\n  fogBuffer->set_sort(geometryBuffer2->get_sort() + 1);\n  fogNP.set_shader(fogShader);\n  fogNP.set_shader_input(\"pi\",               PI_SHADER_INPUT);\n  fogNP.set_shader_input(\"gamma\",            GAMMA_SHADER_INPUT);\n  fogNP.set_shader_input(\"backgroundColor0\", backgroundColor[0]);\n  fogNP.set_shader_input(\"backgroundColor1\", backgroundColor[1]);\n  fogNP.set_shader_input(\"positionTexture0\", positionTexture1);\n  fogNP.set_shader_input(\"positionTexture1\", positionTexture2);\n  fogNP.set_shader_input(\"smokeMaskTexture\", smokeMaskTexture);\n  fogNP.set_shader_input(\"sunPosition\",      LVecBase2f(sunlightP, 0));\n  fogNP.set_shader_input(\"origin\",           cameraNP.get_relative_point(render, environmentNP.get_pos()));\n  fogNP.set_shader_input(\"nearFar\",          LVecBase2f(fogNear, fogFar));\n  fogNP.set_shader_input(\"enabled\",          fogEnabled);\n  fogCamera->set_initial_state(fogNP.get_state());\n  PT(Texture) fogTexture = fogBuffer->get_texture();\n\n  framebufferTextureArguments.clearColor = LColor(1, 1, 1, 0);\n  framebufferTextureArguments.name       = \"ssao\";\n\n  FramebufferTexture ssaoFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) ssaoBuffer = ssaoFramebufferTexture.buffer;\n  PT(Camera)         ssaoCamera = ssaoFramebufferTexture.camera;\n  NodePath           ssaoNP     = ssaoFramebufferTexture.shaderNP;\n  ssaoBuffer->set_sort(geometryBuffer0->get_sort() + 1);\n  ssaoNP.set_shader(ssaoShader);\n  ssaoNP.set_shader_input(\"positionTexture\", positionTexture0);\n  ssaoNP.set_shader_input(\"normalTexture\",   normalTexture0);\n  ssaoNP.set_shader_input(\"samples\",         generateSsaoSamples(SSAO_SAMPLES));\n  ssaoNP.set_shader_input(\"noise\",           generateSsaoNoise(SSAO_NOISE));\n  ssaoNP.set_shader_input(\"lensProjection\",  geometryCameraLens0->get_projection_mat());\n  ssaoNP.set_shader_input(\"enabled\",         ssaoEnabled);\n  ssaoCamera->set_initial_state(ssaoNP.get_state());\n\n  framebufferTextureArguments.name = \"ssaoBlur\";\n\n  FramebufferTexture ssaoBlurFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) ssaoBlurBuffer = ssaoBlurFramebufferTexture.buffer;\n  NodePath           ssaoBlurNP     = ssaoBlurFramebufferTexture.shaderNP;\n  ssaoBlurBuffer->set_sort(ssaoBuffer->get_sort() + 1);\n  ssaoBlurNP.set_shader(kuwaharaFilterShader);\n  ssaoBlurNP.set_shader_input(\"colorTexture\", ssaoBuffer->get_texture());\n  ssaoBlurNP.set_shader_input(\"parameters\",   LVecBase2f(1, 0));\n  ssaoBlurFramebufferTexture.camera->set_initial_state(ssaoBlurNP.get_state());\n  PT(Texture) ssaoBlurTexture = ssaoBlurBuffer->get_texture();\n\n  framebufferTextureArguments.rgbaBits   = rgba16;\n  framebufferTextureArguments.clearColor = LColor(0, 0, 0, 0);\n  framebufferTextureArguments.name       = \"refractionUv\";\n\n  FramebufferTexture refractionUvFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) refractionUvBuffer = refractionUvFramebufferTexture.buffer;\n  PT(Camera)         refractionUvCamera = refractionUvFramebufferTexture.camera;\n  NodePath           refractionUvNP     = refractionUvFramebufferTexture.shaderNP;\n  refractionUvBuffer->set_sort(geometryBuffer1->get_sort() + 1);\n  refractionUvNP.set_shader(screenSpaceRefractionShader);\n  refractionUvNP.set_shader_input(\"positionFromTexture\", positionTexture1);\n  refractionUvNP.set_shader_input(\"positionToTexture\",   positionTexture0);\n  refractionUvNP.set_shader_input(\"normalFromTexture\",   normalTexture1);\n  refractionUvNP.set_shader_input(\"lensProjection\",      geometryCameraLens0->get_projection_mat());\n  refractionUvNP.set_shader_input(\"enabled\",             refractionEnabled);\n  refractionUvNP.set_shader_input(\"rior\",                rior);\n  refractionUvCamera->set_initial_state(refractionUvNP.get_state());\n  PT(Texture) refractionUvTexture = refractionUvBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"reflectionUv\";\n\n  FramebufferTexture reflectionUvFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) reflectionUvBuffer = reflectionUvFramebufferTexture.buffer;\n  PT(Camera)         reflectionUvCamera = reflectionUvFramebufferTexture.camera;\n  NodePath           reflectionUvNP     = reflectionUvFramebufferTexture.shaderNP;\n  reflectionUvBuffer->set_sort(geometryBuffer1->get_sort() + 1);\n  reflectionUvNP.set_shader(screenSpaceReflectionShader);\n  reflectionUvNP.set_shader_input(\"positionTexture\", positionTexture1);\n  reflectionUvNP.set_shader_input(\"normalTexture\",   normalTexture1);\n  reflectionUvNP.set_shader_input(\"maskTexture\",     reflectionMaskTexture);\n  reflectionUvNP.set_shader_input(\"lensProjection\",  geometryCameraLens0->get_projection_mat());\n  reflectionUvNP.set_shader_input(\"enabled\",         reflectionEnabled);\n  reflectionUvCamera->set_initial_state(reflectionUvNP.get_state());\n  PT(Texture) reflectionUvTexture = reflectionUvBuffer->get_texture();\n\n  framebufferTextureArguments.rgbaBits = rgba8;\n  framebufferTextureArguments.aux_rgba = 1;\n  framebufferTextureArguments.useScene = true;\n  framebufferTextureArguments.name     = \"base\";\n\n  FramebufferTexture baseFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) baseBuffer = baseFramebufferTexture.buffer;\n  PT(Camera)         baseCamera = baseFramebufferTexture.camera;\n  NodePath           baseNP     = baseFramebufferTexture.shaderNP;\n  baseBuffer->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_0\n    );\n  baseBuffer->set_clear_active(3, true);\n  baseBuffer->set_clear_value( 3, framebufferTextureArguments.clearColor);\n  baseBuffer->set_sort\n    ( std::max\n        ( ssaoBlurBuffer->get_sort() + 1\n        , UNSORTED_RENDER_SORT_ORDER + 1\n        )\n    );\n  baseNP.set_shader(baseShader);\n  baseNP.set_shader_input(\"pi\",                PI_SHADER_INPUT);\n  baseNP.set_shader_input(\"gamma\",             GAMMA_SHADER_INPUT);\n  baseNP.set_shader_input(\"ssaoBlurTexture\",   ssaoBlurTexture);\n  baseNP.set_shader_input(\"flowTexture\",       stillFlowTexture);\n  baseNP.set_shader_input(\"normalMapsEnabled\", normalMapsEnabled);\n  baseNP.set_shader_input(\"blinnPhongEnabled\", blinnPhongEnabled);\n  baseNP.set_shader_input(\"fresnelEnabled\",    fresnelEnabled);\n  baseNP.set_shader_input(\"rimLightEnabled\",   rimLightEnabled);\n  baseNP.set_shader_input(\"celShadingEnabled\", celShadingEnabled);\n  baseNP.set_shader_input(\"flowMapsEnabled\",   flowMapsEnabled);\n  baseNP.set_shader_input(\"specularOnly\",      LVecBase2f(0, 0));\n  baseNP.set_shader_input(\"isParticle\",        LVecBase2f(0, 0));\n  baseNP.set_shader_input(\"isWater\",           LVecBase2f(0, 0));\n  baseNP.set_shader_input(\"sunPosition\",       LVecBase2f(sunlightP, 0));\n  baseCamera->set_initial_state(baseNP.get_state());\n  baseCamera->set_tag_state_key(\"baseBuffer\");\n  baseCamera->set_tag_state(\"isParticle\", isSmokeNP.get_state());\n  baseCamera->set_tag_state(\"isWater\",    isWaterNP.get_state());\n  baseCamera->set_camera_mask(BitMask32::bit(6));\n  smokeNP.set_tag(\"baseBuffer\", \"isParticle\");\n  waterNP.set_tag(\"baseBuffer\", \"isWater\");\n  PT(Texture) baseTexture     = baseBuffer->get_texture(0);\n  PT(Texture) specularTexture = baseBuffer->get_texture(1);\n\n  framebufferTextureArguments.aux_rgba = 0;\n  framebufferTextureArguments.useScene = false;\n  framebufferTextureArguments.name     = \"refraction\";\n\n  FramebufferTexture refractionFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) refractionBuffer = refractionFramebufferTexture.buffer;\n  PT(Camera)         refractionCamera = refractionFramebufferTexture.camera;\n  NodePath           refractionNP     = refractionFramebufferTexture.shaderNP;\n  refractionBuffer->set_sort(baseBuffer->get_sort() + 1);\n  refractionNP.set_shader(refractionShader);\n  refractionNP.set_shader_input(\"pi\",                     PI_SHADER_INPUT);\n  refractionNP.set_shader_input(\"gamma\",                  GAMMA_SHADER_INPUT);\n  refractionNP.set_shader_input(\"uvTexture\",              refractionUvTexture);\n  refractionNP.set_shader_input(\"maskTexture\",            refractionMaskTexture);\n  refractionNP.set_shader_input(\"positionFromTexture\",    positionTexture1);\n  refractionNP.set_shader_input(\"positionToTexture\",      positionTexture0);\n  refractionNP.set_shader_input(\"backgroundColorTexture\", baseTexture);\n  refractionNP.set_shader_input(\"sunPosition\",            LVecBase2f(sunlightP, 0));\n  refractionCamera->set_initial_state(refractionNP.get_state());\n  PT(Texture) refractionTexture = refractionBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"foam\";\n\n  FramebufferTexture foamFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) foamBuffer = foamFramebufferTexture.buffer;\n  PT(Camera)         foamCamera = foamFramebufferTexture.camera;\n  NodePath           foamNP     = foamFramebufferTexture.shaderNP;\n  foamBuffer->set_sort(geometryBuffer1->get_sort() + 1);\n  foamNP.set_shader(foamShader);\n  foamNP.set_shader_input(\"pi\",                  PI_SHADER_INPUT);\n  foamNP.set_shader_input(\"gamma\",               GAMMA_SHADER_INPUT);\n  foamNP.set_shader_input(\"maskTexture\",         foamMaskTexture);\n  foamNP.set_shader_input(\"foamDepth\",           foamDepth);\n  foamNP.set_shader_input(\"sunPosition\",         LVecBase2f(sunlightP, 0));\n  foamNP.set_shader_input(\"viewWorldMat\",        currentViewWorldMat);\n  foamNP.set_shader_input(\"positionFromTexture\", positionTexture1);\n  foamNP.set_shader_input(\"positionToTexture\",   positionTexture0);\n  foamCamera->set_initial_state(foamNP.get_state());\n  PT(Texture) foamTexture = foamBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"reflectionColor\";\n\n  FramebufferTexture reflectionColorFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) reflectionColorBuffer = reflectionColorFramebufferTexture.buffer;\n  PT(Camera)         reflectionColorCamera = reflectionColorFramebufferTexture.camera;\n  NodePath           reflectionColorNP     = reflectionColorFramebufferTexture.shaderNP;\n  reflectionColorBuffer->set_sort(refractionBuffer->get_sort() + 1);\n  reflectionColorNP.set_shader(reflectionColorShader);\n  reflectionColorNP.set_shader_input(\"colorTexture\", refractionTexture);\n  reflectionColorNP.set_shader_input(\"uvTexture\",    reflectionUvTexture);\n  reflectionColorCamera->set_initial_state(reflectionColorNP.get_state());\n  PT(Texture) reflectionColorTexture = reflectionColorBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"reflectionColorBlur\";\n\n  FramebufferTexture reflectionColorBlurFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) reflectionColorBlurBuffer = reflectionColorBlurFramebufferTexture.buffer;\n  PT(Camera)         reflectionColorBlurCamera = reflectionColorBlurFramebufferTexture.camera;\n  NodePath           reflectionColorBlurNP     = reflectionColorBlurFramebufferTexture.shaderNP;\n  reflectionColorBlurBuffer->set_sort(reflectionColorBuffer->get_sort() + 1);\n  reflectionColorBlurNP.set_shader(boxBlurShader);\n  reflectionColorBlurNP.set_shader_input(\"colorTexture\", reflectionColorTexture);\n  reflectionColorBlurNP.set_shader_input(\"parameters\",   LVecBase2f(8, 1));\n  reflectionColorBlurCamera->set_initial_state(reflectionColorBlurNP.get_state());\n  PT(Texture) reflectionColorBlurTexture = reflectionColorBlurBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"reflection\";\n\n  FramebufferTexture reflectionFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) reflectionBuffer = reflectionFramebufferTexture.buffer;\n  NodePath           reflectionNP     = reflectionFramebufferTexture.shaderNP;\n  reflectionBuffer->set_sort(reflectionColorBlurBuffer->get_sort() + 1);\n  reflectionNP.set_shader(reflectionShader);\n  reflectionNP.set_shader_input(\"colorTexture\",     reflectionColorTexture);\n  reflectionNP.set_shader_input(\"colorBlurTexture\", reflectionColorBlurTexture);\n  reflectionNP.set_shader_input(\"maskTexture\",      reflectionMaskTexture);\n  reflectionFramebufferTexture.camera->set_initial_state(reflectionNP.get_state());\n  PT(Texture) reflectionTexture = reflectionBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"baseCombine\";\n\n  FramebufferTexture baseCombineFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) baseCombineBuffer = baseCombineFramebufferTexture.buffer;\n  PT(Camera)         baseCombineCamera = baseCombineFramebufferTexture.camera;\n  NodePath           baseCombineNP     = baseCombineFramebufferTexture.shaderNP;\n  baseCombineBuffer->set_sort(reflectionBuffer->get_sort() + 1);\n  baseCombineNP.set_shader(baseCombineShader);\n  baseCombineNP.set_shader_input(\"baseTexture\",       baseTexture);\n  baseCombineNP.set_shader_input(\"refractionTexture\", refractionTexture);\n  baseCombineNP.set_shader_input(\"foamTexture\",       foamTexture);\n  baseCombineNP.set_shader_input(\"reflectionTexture\", reflectionTexture);\n  baseCombineNP.set_shader_input(\"specularTexture\",   specularTexture);\n  baseCombineCamera->set_initial_state(baseCombineNP.get_state());\n  PT(Texture) baseCombineTexture = baseCombineBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"sharpen\";\n\n  FramebufferTexture sharpenFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) sharpenBuffer = sharpenFramebufferTexture.buffer;\n  NodePath           sharpenNP     = sharpenFramebufferTexture.shaderNP;\n  sharpenBuffer->set_sort(baseCombineBuffer->get_sort() + 1);\n  sharpenNP.set_shader(sharpenShader);\n  sharpenNP.set_shader_input(\"colorTexture\", baseCombineTexture);\n  sharpenNP.set_shader_input(\"enabled\",      sharpenEnabled);\n  PT(Camera) sharpenCamera = sharpenFramebufferTexture.camera;\n  sharpenCamera->set_initial_state(sharpenNP.get_state());\n  PT(Texture) sharpenTexture = sharpenBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"posterize\";\n\n  FramebufferTexture posterizeFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) posterizeBuffer = posterizeFramebufferTexture.buffer;\n  NodePath           posterizeNP     = posterizeFramebufferTexture.shaderNP;\n  posterizeBuffer->set_sort(sharpenBuffer->get_sort() + 1);\n  posterizeNP.set_shader(posterizeShader);\n  posterizeNP.set_shader_input(\"gamma\",           GAMMA_SHADER_INPUT);\n  posterizeNP.set_shader_input(\"colorTexture\",    sharpenTexture);\n  posterizeNP.set_shader_input(\"positionTexture\", positionTexture2);\n  posterizeNP.set_shader_input(\"enabled\",         posterizeEnabled);\n  PT(Camera) posterizeCamera = posterizeFramebufferTexture.camera;\n  posterizeCamera->set_initial_state(posterizeNP.get_state());\n  PT(Texture) posterizeTexture = posterizeBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"bloom\";\n\n  FramebufferTexture bloomFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) bloomBuffer = bloomFramebufferTexture.buffer;\n  PT(Camera)         bloomCamera = bloomFramebufferTexture.camera;\n  NodePath           bloomNP     = bloomFramebufferTexture.shaderNP;\n  bloomBuffer->set_sort(posterizeBuffer->get_sort() + 1);\n  bloomNP.set_shader(bloomShader);\n  bloomNP.set_shader_input(\"colorTexture\", posterizeTexture);\n  bloomNP.set_shader_input(\"enabled\",      bloomEnabled);\n  bloomCamera->set_initial_state(bloomNP.get_state());\n  PT(Texture) bloomTexture = bloomBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"sceneCombine\";\n\n  FramebufferTexture sceneCombineFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) sceneCombineBuffer = sceneCombineFramebufferTexture.buffer;\n  PT(Camera)         sceneCombineCamera = sceneCombineFramebufferTexture.camera;\n  NodePath           sceneCombineNP     = sceneCombineFramebufferTexture.shaderNP;\n  sceneCombineBuffer->set_sort(bloomBuffer->get_sort() + 1);\n  sceneCombineNP.set_shader(sceneCombineShader);\n  sceneCombineNP.set_shader_input(\"pi\",                  PI_SHADER_INPUT);\n  sceneCombineNP.set_shader_input(\"gamma\",               GAMMA_SHADER_INPUT);\n  sceneCombineNP.set_shader_input(\"lookupTableTextureN\", colorLookupTableTextureN);\n  sceneCombineNP.set_shader_input(\"backgroundColor0\",    backgroundColor[0]);\n  sceneCombineNP.set_shader_input(\"backgroundColor1\",    backgroundColor[1]);\n  sceneCombineNP.set_shader_input(\"baseTexture\",         posterizeTexture);\n  sceneCombineNP.set_shader_input(\"bloomTexture\",        bloomTexture);\n  sceneCombineNP.set_shader_input(\"fogTexture\",          fogTexture);\n  sceneCombineNP.set_shader_input(\"sunPosition\",         LVecBase2f(sunlightP, 0));\n  PT(Texture) sceneCombineTexture = sceneCombineBuffer->get_texture();\n  sceneCombineCamera->set_initial_state(sceneCombineNP.get_state());\n\n  framebufferTextureArguments.clearColor = backgroundColor[1];\n  framebufferTextureArguments.name       = \"outOfFocus\";\n\n  FramebufferTexture outOfFocusFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) outOfFocusBuffer = outOfFocusFramebufferTexture.buffer;\n  PT(Camera)         outOfFocusCamera = outOfFocusFramebufferTexture.camera;\n  NodePath           outOfFocusNP     = outOfFocusFramebufferTexture.shaderNP;\n  outOfFocusBuffer->set_sort(sceneCombineBuffer->get_sort() + 1);\n  outOfFocusNP.set_shader(boxBlurShader);\n  outOfFocusNP.set_shader_input(\"colorTexture\", sceneCombineTexture);\n  outOfFocusNP.set_shader_input(\"parameters\",   LVecBase2f(2, 2));\n  outOfFocusCamera->set_initial_state(outOfFocusNP.get_state());\n  PT(Texture) outOfFocusTexture = outOfFocusBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"dilatedOutOfFocus\";\n\n  FramebufferTexture dilatedOutOfFocusFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) dilatedOutOfFocusBuffer = dilatedOutOfFocusFramebufferTexture.buffer;\n  PT(Camera)         dilatedOutOfFocusCamera = dilatedOutOfFocusFramebufferTexture.camera;\n  NodePath           dilatedOutOfFocusNP     = dilatedOutOfFocusFramebufferTexture.shaderNP;\n  dilatedOutOfFocusBuffer->set_sort(outOfFocusBuffer->get_sort() + 1);\n  dilatedOutOfFocusNP.set_shader(dilationShader);\n  dilatedOutOfFocusNP.set_shader_input(\"colorTexture\", outOfFocusTexture);\n  dilatedOutOfFocusNP.set_shader_input(\"parameters\",   LVecBase2f(4, 2));\n  dilatedOutOfFocusCamera->set_initial_state(dilatedOutOfFocusNP.get_state());\n  PT(Texture) dilatedOutOfFocusTexture = dilatedOutOfFocusBuffer->get_texture();\n\n  framebufferTextureArguments.aux_rgba = 1;\n  framebufferTextureArguments.name     = \"depthOfField\";\n\n  FramebufferTexture depthOfFieldFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) depthOfFieldBuffer = depthOfFieldFramebufferTexture.buffer;\n  NodePath           depthOfFieldNP     = depthOfFieldFramebufferTexture.shaderNP;\n  depthOfFieldBuffer->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , GraphicsOutput::RTP_aux_rgba_0\n    );\n  depthOfFieldBuffer->set_clear_active(3, true);\n  depthOfFieldBuffer->set_clear_value( 3, framebufferTextureArguments.clearColor);\n  depthOfFieldBuffer->set_sort(dilatedOutOfFocusBuffer->get_sort() + 1);\n  depthOfFieldNP.set_shader(depthOfFieldShader);\n  depthOfFieldNP.set_shader_input(\"positionTexture\",   positionTexture0);\n  depthOfFieldNP.set_shader_input(\"focusTexture\",      sceneCombineTexture);\n  depthOfFieldNP.set_shader_input(\"outOfFocusTexture\", dilatedOutOfFocusTexture);\n  depthOfFieldNP.set_shader_input(\"mouseFocusPoint\",   mouseFocusPoint);\n  depthOfFieldNP.set_shader_input(\"nearFar\",           cameraNearFar);\n  depthOfFieldNP.set_shader_input(\"enabled\",           depthOfFieldEnabled);\n  PT(Camera) depthOfFieldCamera = depthOfFieldFramebufferTexture.camera;\n  depthOfFieldCamera->set_initial_state(depthOfFieldNP.get_state());\n  PT(Texture) depthOfFieldTexture0 = depthOfFieldBuffer->get_texture(0);\n  PT(Texture) depthOfFieldTexture1 = depthOfFieldBuffer->get_texture(1);\n\n  framebufferTextureArguments.aux_rgba = 0;\n  framebufferTextureArguments.name     = \"outline\";\n\n  FramebufferTexture outlineFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) outlineBuffer = outlineFramebufferTexture.buffer;\n  PT(Camera)         outlineCamera = outlineFramebufferTexture.camera;\n  NodePath           outlineNP     = outlineFramebufferTexture.shaderNP;\n  outlineBuffer->set_sort(depthOfFieldBuffer->get_sort() + 1);\n  outlineNP.set_shader(outlineShader);\n  outlineNP.set_shader_input(\"gamma\",               GAMMA_SHADER_INPUT);\n  outlineNP.set_shader_input(\"positionTexture\",     positionTexture0);\n  outlineNP.set_shader_input(\"colorTexture\",        depthOfFieldTexture0);\n  outlineNP.set_shader_input(\"noiseTexture\",        colorNoiseTexture);\n  outlineNP.set_shader_input(\"depthOfFieldTexture\", depthOfFieldTexture1);\n  outlineNP.set_shader_input(\"fogTexture\",          fogTexture);\n  outlineNP.set_shader_input(\"nearFar\",             cameraNearFar);\n  outlineNP.set_shader_input(\"enabled\",             outlineEnabled);\n  outlineCamera->set_initial_state(outlineNP.get_state());\n  PT(Texture) outlineTexture = outlineBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"painterly\";\n\n  FramebufferTexture painterlyFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) painterlyBuffer = painterlyFramebufferTexture.buffer;\n  NodePath           painterlyNP     = painterlyFramebufferTexture.shaderNP;\n  painterlyBuffer->set_sort(outlineBuffer->get_sort() + 1);\n  painterlyNP.set_shader(kuwaharaFilterShader);\n  painterlyNP.set_shader_input(\"colorTexture\", outlineTexture);\n  painterlyNP.set_shader_input(\"parameters\",   LVecBase2f(0, 0));\n  PT(Camera) painterlyCamera = painterlyFramebufferTexture.camera;\n  painterlyCamera->set_initial_state(painterlyNP.get_state());\n  PT(Texture) painterlyTexture = painterlyBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"pixelize\";\n\n  FramebufferTexture pixelizeFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) pixelizeBuffer = pixelizeFramebufferTexture.buffer;\n  NodePath           pixelizeNP     = pixelizeFramebufferTexture.shaderNP;\n  pixelizeBuffer->set_sort(painterlyBuffer->get_sort() + 1);\n  pixelizeNP.set_shader(pixelizeShader);\n  pixelizeNP.set_shader_input(\"colorTexture\",    painterlyTexture);\n  pixelizeNP.set_shader_input(\"positionTexture\", positionTexture2);\n  pixelizeNP.set_shader_input(\"parameters\",      LVecBase2f(5, 0));\n  pixelizeNP.set_shader_input(\"enabled\",         pixelizeEnabled);\n  PT(Camera) pixelizeCamera = pixelizeFramebufferTexture.camera;\n  pixelizeCamera->set_initial_state(pixelizeNP.get_state());\n  PT(Texture) pixelizeTexture = pixelizeBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"motionBlur\";\n\n  FramebufferTexture motionBlurFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) motionBlurBuffer = motionBlurFramebufferTexture.buffer;\n  NodePath           motionBlurNP     = motionBlurFramebufferTexture.shaderNP;\n  motionBlurBuffer->set_sort(pixelizeBuffer->get_sort() + 1);\n  motionBlurNP.set_shader(motionBlurShader);\n  motionBlurNP.set_shader_input(\"previousViewWorldMat\",    previousViewWorldMat);\n  motionBlurNP.set_shader_input(\"worldViewMat\",            render.get_transform(cameraNP)->get_mat());\n  motionBlurNP.set_shader_input(\"lensProjection\",          geometryCameraLens2->get_projection_mat());\n  motionBlurNP.set_shader_input(\"positionTexture\",         positionTexture2);\n  motionBlurNP.set_shader_input(\"colorTexture\",            pixelizeTexture);\n  motionBlurNP.set_shader_input(\"motionBlurEnabled\",       motionBlurEnabled);\n  motionBlurNP.set_shader_input(\"parameters\",              LVecBase2f(2, 1.0));\n  PT(Camera) motionBlurCamera = motionBlurFramebufferTexture.camera;\n  motionBlurCamera->set_initial_state(motionBlurNP.get_state());\n  PT(Texture) motionBlurTexture = motionBlurBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"filmGrain\";\n\n  FramebufferTexture filmGrainFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) filmGrainBuffer = filmGrainFramebufferTexture.buffer;\n  NodePath           filmGrainNP     = filmGrainFramebufferTexture.shaderNP;\n  filmGrainBuffer->set_sort(motionBlurBuffer->get_sort() + 1);\n  filmGrainNP.set_shader(filmGrainShader);\n  filmGrainNP.set_shader_input(\"pi\",           PI_SHADER_INPUT);\n  filmGrainNP.set_shader_input(\"colorTexture\", motionBlurTexture);\n  filmGrainNP.set_shader_input(\"enabled\",      filmGrainEnabled);\n  PT(Camera) filmGrainCamera = filmGrainFramebufferTexture.camera;\n  filmGrainCamera->set_initial_state(filmGrainNP.get_state());\n  PT(Texture) filmGrainTexture = filmGrainBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"lookupTable\";\n\n  FramebufferTexture lookupTableFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) lookupTableBuffer = lookupTableFramebufferTexture.buffer;\n  NodePath           lookupTableNP     = lookupTableFramebufferTexture.shaderNP;\n  lookupTableBuffer->set_sort(filmGrainBuffer->get_sort() + 1);\n  lookupTableNP.set_shader(lookupTableShader);\n  lookupTableNP.set_shader_input(\"pi\",                  PI_SHADER_INPUT);\n  lookupTableNP.set_shader_input(\"gamma\",               GAMMA_SHADER_INPUT);\n  lookupTableNP.set_shader_input(\"colorTexture\",        filmGrainTexture);\n  lookupTableNP.set_shader_input(\"lookupTableTextureN\", colorLookupTableTextureN);\n  lookupTableNP.set_shader_input(\"lookupTableTexture0\", colorLookupTableTexture0);\n  lookupTableNP.set_shader_input(\"lookupTableTexture1\", colorLookupTableTexture1);\n  lookupTableNP.set_shader_input(\"sunPosition\",         LVecBase2f(sunlightP, 0));\n  lookupTableNP.set_shader_input(\"enabled\",             lookupTableEnabled);\n  PT(Camera) lookupTableCamera = lookupTableFramebufferTexture.camera;\n  lookupTableCamera->set_initial_state(lookupTableNP.get_state());\n  PT(Texture) lookupTableTexture = lookupTableBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"gammaCorrection\";\n\n  FramebufferTexture gammaCorrectionFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) gammaCorrectionBuffer = gammaCorrectionFramebufferTexture.buffer;\n  NodePath           gammaCorrectionNP     = gammaCorrectionFramebufferTexture.shaderNP;\n  gammaCorrectionBuffer->set_sort(lookupTableBuffer->get_sort() + 1);\n  gammaCorrectionNP.set_shader(gammaCorrectionShader);\n  gammaCorrectionNP.set_shader_input(\"gamma\",        GAMMA_SHADER_INPUT);\n  gammaCorrectionNP.set_shader_input(\"colorTexture\", lookupTableTexture);\n  PT(Camera) gammaCorrectionCamera = gammaCorrectionFramebufferTexture.camera;\n  gammaCorrectionCamera->set_initial_state(gammaCorrectionNP.get_state());\n  PT(Texture) gammaCorrectionTexture = gammaCorrectionBuffer->get_texture();\n\n  framebufferTextureArguments.name = \"chromaticAberration\";\n\n  FramebufferTexture chromaticAberrationFramebufferTexture =\n    generateFramebufferTexture\n      ( framebufferTextureArguments\n      );\n  PT(GraphicsOutput) chromaticAberrationBuffer = chromaticAberrationFramebufferTexture.buffer;\n  NodePath           chromaticAberrationNP     = chromaticAberrationFramebufferTexture.shaderNP;\n  chromaticAberrationBuffer->set_sort(gammaCorrectionBuffer->get_sort() + 1);\n  chromaticAberrationNP.set_shader(chromaticAberrationShader);\n  chromaticAberrationNP.set_shader_input(\"mouseFocusPoint\", mouseFocusPoint);\n  chromaticAberrationNP.set_shader_input(\"colorTexture\",    gammaCorrectionTexture);\n  chromaticAberrationNP.set_shader_input(\"enabled\",         chromaticAberrationEnabled);\n  PT(Camera) chromaticAberrationCamera = chromaticAberrationFramebufferTexture.camera;\n  chromaticAberrationCamera->set_initial_state(chromaticAberrationNP.get_state());\n\n  graphicsOutput->set_sort(chromaticAberrationBuffer->get_sort() + 1);\n\n  int showBufferIndex = 0;\n\n  std::vector<std::tuple<std::string, PT(GraphicsOutput), int>> bufferArray =\n    { std::make_tuple(\"Positions 0\",          geometryBuffer0,           0)\n    , std::make_tuple(\"Normals 0\",            geometryBuffer0,           1)\n    , std::make_tuple(\"Positions 1\",          geometryBuffer1,           0)\n    , std::make_tuple(\"Normals 1\",            geometryBuffer1,           1)\n    , std::make_tuple(\"Reflection Mask\",      geometryBuffer1,           2)\n    , std::make_tuple(\"Refraction Mask\",      geometryBuffer1,           3)\n    , std::make_tuple(\"Foam Mask\",            geometryBuffer1,           4)\n    , std::make_tuple(\"Positions 2\",          geometryBuffer2,           0)\n    , std::make_tuple(\"Smoke Mask\",           geometryBuffer2,           1)\n    , std::make_tuple(\"SSAO\",                 ssaoBuffer,                0)\n    , std::make_tuple(\"SSAO Blur\",            ssaoBlurBuffer,            0)\n    , std::make_tuple(\"Refraction UV\",        refractionUvBuffer,        0)\n    , std::make_tuple(\"Refraction\",           refractionBuffer,          0)\n    , std::make_tuple(\"Reflection UV\",        reflectionUvBuffer,        0)\n    , std::make_tuple(\"Reflection Color\",     reflectionColorBuffer,     0)\n    , std::make_tuple(\"Reflection Blur\",      reflectionColorBlurBuffer, 0)\n    , std::make_tuple(\"Reflection\",           reflectionBuffer,          0)\n    , std::make_tuple(\"Foam\",                 foamBuffer,                0)\n    , std::make_tuple(\"Base\",                 baseBuffer,                0)\n    , std::make_tuple(\"Specular\",             baseBuffer,                1)\n    , std::make_tuple(\"Base Combine\",         baseCombineBuffer,         0)\n    , std::make_tuple(\"Painterly\",            painterlyBuffer,           0)\n    , std::make_tuple(\"Posterize\",            posterizeBuffer,           0)\n    , std::make_tuple(\"Bloom\",                bloomBuffer,               0)\n    , std::make_tuple(\"Outline\",              outlineBuffer,             0)\n    , std::make_tuple(\"Fog\",                  fogBuffer,                 0)\n    , std::make_tuple(\"Scene Combine\",        sceneCombineBuffer,        0)\n    , std::make_tuple(\"Out of Focus\",         outOfFocusBuffer,          0)\n    , std::make_tuple(\"Dilation\",             dilatedOutOfFocusBuffer,   0)\n    , std::make_tuple(\"Depth of Field Blur\",  depthOfFieldBuffer,        1)\n    , std::make_tuple(\"Depth of Field\",       depthOfFieldBuffer,        0)\n    , std::make_tuple(\"Pixelize\",             pixelizeBuffer,            0)\n    , std::make_tuple(\"Motion Blur\",          motionBlurBuffer,          0)\n    , std::make_tuple(\"Film Grain\",           filmGrainBuffer,           0)\n    , std::make_tuple(\"Lookup Table\",         lookupTableBuffer,         0)\n    , std::make_tuple(\"Gamma Correction\",     gammaCorrectionBuffer,     0)\n    , std::make_tuple(\"Chromatic Aberration\", chromaticAberrationBuffer, 0)\n    };\n\n  showBufferIndex = bufferArray.size() - 1;\n\n  showBuffer\n    ( render2d\n    , statusNP\n    , bufferArray[showBufferIndex]\n    , false\n    );\n\n  shuttersAnimationCollection.play(   \"close-shutters\"          );\n  weatherVaneAnimationCollection.loop(\"weather-vane-shake\", true);\n  bannerAnimationCollection.loop(     \"banner-swing\",       true);\n\n  int then          = microsecondsSinceEpoch();\n  int loopStartedAt = then;\n  int now           = then;\n  int keyTime       = now;\n\n  auto beforeFrame =\n    [&]() -> void {\n\n    WindowProperties windowProperties = graphicsWindow->get_properties();\n    if (windowProperties.get_minimized()) {\n      std::this_thread::sleep_for(std::chrono::seconds(1));\n    }\n\n    now = microsecondsSinceEpoch();\n\n    // Avoids a loud audio pop.\n    if (!soundStarted && microsecondToSecond(now - loopStartedAt) >= startSoundAt) {\n      for_each\n        ( sounds.begin()\n        , sounds.end()\n        , [](PT(AudioSound) sound) { sound->set_loop(true); sound->play(); }\n        );\n      soundStarted = true;\n    }\n\n    double delta = microsecondToSecond(now - then);\n\n    then = now;\n\n    double movement = 100 * delta;\n\n    double timeSinceKey = microsecondToSecond(now - keyTime);\n    bool   keyDebounced = timeSinceKey >= 0.2;\n\n    double cameraUpDownAdjust    = 0;\n    double cameraLeftRightAdjust = 0;\n\n    bool shiftDown               = isButtonDown(mouseWatcher, \"shift\");\n    bool tabDown                 = isButtonDown(mouseWatcher, \"tab\");\n\n    bool resetDown               = isButtonDown(mouseWatcher, \"r\");\n\n    bool fogNearDown             = isButtonDown(mouseWatcher, \"[\");\n    bool fogFarDown              = isButtonDown(mouseWatcher, \"]\");\n\n    bool equalDown               = isButtonDown(mouseWatcher, \"=\");\n    bool minusDown               = isButtonDown(mouseWatcher, \"-\");\n\n    bool deleteDown              = isButtonDown(mouseWatcher, \"delete\");\n\n    bool wDown                   = isButtonDown(mouseWatcher, \"w\");\n    bool aDown                   = isButtonDown(mouseWatcher, \"a\");\n    bool dDown                   = isButtonDown(mouseWatcher, \"d\");\n    bool sDown                   = isButtonDown(mouseWatcher, \"s\");\n    bool zDown                   = isButtonDown(mouseWatcher, \"z\");\n    bool xDown                   = isButtonDown(mouseWatcher, \"x\");\n\n    bool arrowUpDown             = isButtonDown(mouseWatcher, \"arrow_up\");\n    bool arrowDownDown           = isButtonDown(mouseWatcher, \"arrow_down\");\n    bool arrowLeftDown           = isButtonDown(mouseWatcher, \"arrow_left\");\n    bool arrowRightDown          = isButtonDown(mouseWatcher, \"arrow_right\");\n\n    bool middayDown              = isButtonDown(mouseWatcher, \"1\");\n    bool midnightDown            = isButtonDown(mouseWatcher, \"2\");\n    bool fresnelDown             = isButtonDown(mouseWatcher, \"3\");\n    bool rimLightDown            = isButtonDown(mouseWatcher, \"4\");\n    bool particlesDown           = isButtonDown(mouseWatcher, \"5\");\n    bool motionBlurDown          = isButtonDown(mouseWatcher, \"6\");\n    bool painterlyDown           = isButtonDown(mouseWatcher, \"7\");\n    bool celShadingDown          = isButtonDown(mouseWatcher, \"8\");\n    bool lookupTableDown         = isButtonDown(mouseWatcher, \"9\");\n    bool blinnPhongDown          = isButtonDown(mouseWatcher, \"0\");\n    bool ssaoDown                = isButtonDown(mouseWatcher, \"y\");\n    bool outlineDown             = isButtonDown(mouseWatcher, \"u\");\n    bool bloomDown               = isButtonDown(mouseWatcher, \"i\");\n    bool normalMapsDown          = isButtonDown(mouseWatcher, \"o\");\n    bool fogDown                 = isButtonDown(mouseWatcher, \"p\");\n    bool depthOfFieldDown        = isButtonDown(mouseWatcher, \"h\");\n    bool posterizeDown           = isButtonDown(mouseWatcher, \"j\");\n    bool pixelizeDown            = isButtonDown(mouseWatcher, \"k\");\n    bool sharpenDown             = isButtonDown(mouseWatcher, \"l\");\n    bool filmGrainDown           = isButtonDown(mouseWatcher, \"n\");\n    bool reflectionDown          = isButtonDown(mouseWatcher, \"m\");\n    bool refractionDown          = isButtonDown(mouseWatcher, \",\");\n    bool flowMapsDown            = isButtonDown(mouseWatcher, \".\");\n    bool sunlightDown            = isButtonDown(mouseWatcher, \"/\");\n    bool chromaticAberrationDown = isButtonDown(mouseWatcher, \"\\\\\");\n\n    bool mouseLeftDown    = mouseWatcher->is_button_down(MouseButton::one());\n    bool mouseMiddleDown  = mouseWatcher->is_button_down(MouseButton::two());\n    bool mouseRightDown   = mouseWatcher->is_button_down(MouseButton::three());\n\n    if (wDown) {\n      cameraRotatePhi -= movement * 0.5;\n    }\n\n    if (sDown) {\n      cameraRotatePhi += movement * 0.5;\n    }\n\n    if (aDown) {\n      cameraRotateTheta += movement * 0.5;\n    }\n\n    if (dDown) {\n      cameraRotateTheta -= movement * 0.5;\n    }\n\n    if (zDown || mouseWheelUp) {\n      cameraRotateRadius -= movement * 4 + 50 * mouseWheelUp;\n      mouseWheelUp = false;\n    }\n\n    if (xDown || mouseWheelDown) {\n      cameraRotateRadius += movement * 4 + 50 * mouseWheelDown;\n      mouseWheelDown = false;\n    }\n\n    if (cameraRotatePhi    <  1)               cameraRotatePhi    = 1;\n    if (cameraRotatePhi    >  179)             cameraRotatePhi    = 179;\n    if (cameraRotatePhi    <  0)               cameraRotatePhi    = 360 - cameraRotateTheta;\n    if (cameraRotateTheta  >  360)             cameraRotateTheta  = cameraRotateTheta - 360;\n    if (cameraRotateTheta  <  0)               cameraRotateTheta  = 360 - cameraRotateTheta;\n    if (cameraRotateRadius < cameraNear +  5)  cameraRotateRadius = cameraNear +  5;\n    if (cameraRotateRadius > cameraFar  - 10)  cameraRotateRadius = cameraFar  - 10;\n\n    if (arrowUpDown) {\n      cameraUpDownAdjust = -2 * delta;\n    } else if (arrowDownDown) {\n      cameraUpDownAdjust =  2 * delta;\n    }\n\n    if (arrowLeftDown) {\n      cameraLeftRightAdjust =  2 * delta;\n    } else if (arrowRightDown) {\n      cameraLeftRightAdjust = -2 * delta;\n    }\n\n    if (mouseWatcher->has_mouse()) {\n      mouseNow[0] = mouseWatcher->get_mouse_x();\n      mouseNow[1] = mouseWatcher->get_mouse_y();\n\n      if (mouseLeftDown) {\n        cameraRotateTheta += (mouseThen[0] - mouseNow[0] ) * movement;\n        cameraRotatePhi   += (mouseNow[1]  - mouseThen[1]) * movement;\n      } else if (mouseRightDown) {\n        cameraLeftRightAdjust = (mouseThen[0] - mouseNow[0]) * movement;\n        cameraUpDownAdjust    = (mouseThen[1] - mouseNow[1]) * movement;\n      } else if (mouseMiddleDown) {\n        mouseFocusPoint =\n          LVecBase2f\n            ( (mouseNow[0] + 1.0) / 2.0\n            , (mouseNow[1] + 1.0) / 2.0\n            );\n      }\n\n      if (!mouseLeftDown) {\n        mouseThen = mouseNow;\n      }\n    }\n\n    if (shiftDown && fogNearDown) {\n      fogNear += fogAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Fog Near \" + std::to_string(fogNear);\n    } else if (fogNearDown) {\n      fogNear -= fogAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Fog Near \" + std::to_string(fogNear);\n    }\n\n    if (shiftDown && fogFarDown) {\n      fogFar -= fogAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Fog Far \" + std::to_string(fogFar);\n    } else if (fogFarDown) {\n      fogFar += fogAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Fog Far \" + std::to_string(fogFar);\n    }\n\n    if (shiftDown && equalDown) {\n      rior[0] -= riorAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Refractive Index \" + std::to_string(rior[0]);\n    } else if (equalDown) {\n      rior[0] += riorAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Refractive Index \" + std::to_string(rior[0]);\n    }\n    rior[1] = rior[0];\n\n    if (shiftDown && minusDown) {\n      foamDepth[0] -= foamDepthAdjust;\n      if (foamDepth[0] < 0.001) { foamDepth[0] = 0.001; };\n\n      statusAlpha = 1.0;\n      statusText  = \"Foam Depth \" + std::to_string(foamDepth[0]);\n    } else if (minusDown) {\n      foamDepth[0] += foamDepthAdjust;\n\n      statusAlpha = 1.0;\n      statusText  = \"Foam Depth \" + std::to_string(foamDepth[0]);\n    }\n    foamDepth[1] = foamDepth[0];\n\n    if (keyDebounced) {\n      if (tabDown) {\n        if (shiftDown) {\n          showBufferIndex -= 1;\n          if (showBufferIndex < 0) showBufferIndex = bufferArray.size() - 1;\n        } else {\n          showBufferIndex += 1;\n          if (showBufferIndex >= bufferArray.size()) showBufferIndex = 0;\n        }\n\n        std::string bufferName = std::get<0>(bufferArray[showBufferIndex]);\n        bool showAlpha =\n              bufferName == \"Outline\"\n          ||  bufferName == \"Foam\"\n          ||  bufferName == \"Fog\"\n          ;\n\n        showBuffer\n          ( render2d\n          , statusNP\n          , bufferArray[showBufferIndex]\n          , showAlpha\n          );\n\n        keyTime = now;\n\n        statusAlpha = 1.0;\n        statusText  = bufferName + \" Buffer\";\n      }\n\n      if (resetDown) {\n        cameraRotateRadius = cameraRotateRadiusInitial;\n        cameraRotatePhi    = cameraRotatePhiInitial;\n        cameraRotateTheta  = cameraRotateThetaInitial;\n        cameraLookAt       = cameraLookAtInitial;\n\n        fogNear = fogNearInitial;\n        fogFar  = fogFarInitial;\n\n        foamDepth = foamDepthInitial;\n        rior      = riorInitial;\n\n        mouseFocusPoint = mouseFocusPointInitial;\n\n        keyTime = now;\n\n        statusAlpha = 1.0;\n        statusText  = \"Reset\";\n      }\n\n      auto toggleStatus =\n        [&](LVecBase2f enabled, std::string effect) -> void {\n          statusAlpha = 1.0;\n          if (enabled[0] == 1) {\n            statusText = effect + \" On\";\n          } else {\n            statusText = effect + \" Off\";\n          }\n        };\n\n      if (ssaoDown) {\n        ssaoEnabled = toggleEnabledVec(ssaoEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( ssaoEnabled\n          , \"SSAO\"\n          );\n      }\n\n      if (refractionDown) {\n        refractionEnabled = toggleEnabledVec(refractionEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( refractionEnabled\n          , \"Refraction\"\n          );\n      }\n\n      if (reflectionDown) {\n        reflectionEnabled = toggleEnabledVec(reflectionEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( reflectionEnabled\n          , \"Reflection\"\n          );\n      }\n\n      if (bloomDown) {\n        bloomEnabled = toggleEnabledVec(bloomEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( bloomEnabled\n          , \"Bloom\"\n          );\n      }\n\n      if (normalMapsDown){\n        normalMapsEnabled = toggleEnabledVec(normalMapsEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( normalMapsEnabled\n          , \"Normal Maps\"\n          );\n      }\n\n      if (fogDown) {\n        fogEnabled = toggleEnabledVec(fogEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( fogEnabled\n          , \"Fog\"\n          );\n      }\n\n      if (outlineDown) {\n        outlineEnabled = toggleEnabledVec(outlineEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( outlineEnabled\n          , \"Outline\"\n          );\n      }\n\n      if (celShadingDown) {\n        celShadingEnabled = toggleEnabledVec(celShadingEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( celShadingEnabled\n          , \"Cel Shading\"\n          );\n      }\n\n      if (lookupTableDown) {\n        lookupTableEnabled = toggleEnabledVec(lookupTableEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( lookupTableEnabled\n          , \"Lookup Table\"\n          );\n      }\n\n      if (fresnelDown) {\n        fresnelEnabled = toggleEnabledVec(fresnelEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( fresnelEnabled\n          , \"Fresnel\"\n          );\n      }\n\n      if (rimLightDown) {\n        rimLightEnabled = toggleEnabledVec(rimLightEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( rimLightEnabled\n          , \"Rim Light\"\n          );\n      }\n\n      if (blinnPhongDown) {\n        blinnPhongEnabled = toggleEnabledVec(blinnPhongEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( blinnPhongEnabled\n          , \"Blinn-Phong\"\n          );\n      }\n\n      if (sharpenDown) {\n        sharpenEnabled = toggleEnabledVec(sharpenEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( sharpenEnabled\n          , \"Sharpen\"\n          );\n      }\n\n      if (depthOfFieldDown) {\n        depthOfFieldEnabled = toggleEnabledVec(depthOfFieldEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( depthOfFieldEnabled\n          , \"Depth of Field\"\n          );\n      }\n\n      if (painterlyDown) {\n        painterlyEnabled = toggleEnabledVec(painterlyEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( painterlyEnabled\n          , \"Painterly\"\n          );\n      }\n\n      if (motionBlurDown) {\n        motionBlurEnabled = toggleEnabledVec(motionBlurEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( motionBlurEnabled\n          , \"Motion Blur\"\n          );\n      }\n\n      if (posterizeDown) {\n        posterizeEnabled = toggleEnabledVec(posterizeEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( posterizeEnabled\n          , \"Posterize\"\n          );\n      }\n\n      if (pixelizeDown) {\n        pixelizeEnabled = toggleEnabledVec(pixelizeEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( pixelizeEnabled\n          , \"Pixelize\"\n          );\n      }\n\n      if (filmGrainDown) {\n        filmGrainEnabled = toggleEnabledVec(filmGrainEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( filmGrainEnabled\n          , \"Film Grain\"\n          );\n      }\n\n      if (flowMapsDown) {\n        flowMapsEnabled = toggleEnabledVec(flowMapsEnabled);\n        if (flowMapsEnabled[0] == 1 && soundEnabled) {\n          for_each(sounds.begin(), sounds.end(), setSoundOn);\n        } else if (flowMapsEnabled[0] != 1) {\n          for_each(sounds.begin(), sounds.end(), setSoundOff);\n        }\n        keyTime = now;\n\n        toggleStatus\n          ( flowMapsEnabled\n          , \"Flow Maps\"\n          );\n      }\n\n      if (deleteDown) {\n        if (soundEnabled) {\n          for_each(sounds.begin(), sounds.end(), setSoundOff);\n          soundEnabled = false;\n        } else {\n          if (flowMapsEnabled[0] == 1) {\n            for_each(sounds.begin(), sounds.end(), setSoundOn);\n          }\n          soundEnabled = true;\n        }\n        keyTime = now;\n\n        toggleStatus\n          ( LVecBase2f(soundEnabled ? 1 : 0, 0)\n          , \"Sound\"\n          );\n      }\n\n      if (sunlightDown) {\n        animateSunlight = animateSunlight ? false : true;\n        keyTime = now;\n\n        toggleStatus\n          ( LVecBase2f(animateSunlight ? 1 : 0, 0)\n          , \"Sun Animation\"\n          );\n      }\n\n      if (particlesDown) {\n        keyTime = now;\n        statusAlpha = 1.0;\n\n        if (smokeNP.is_hidden()) {\n          smokeNP.show();\n          statusText = \"Particles On\";\n        } else {\n          smokeNP.hide();\n          statusText = \"Particles Off\";\n        }\n      }\n\n      if (chromaticAberrationDown) {\n        chromaticAberrationEnabled = toggleEnabledVec(chromaticAberrationEnabled);\n        keyTime = now;\n\n        toggleStatus\n          ( chromaticAberrationEnabled\n          , \"Chromatic Aberration\"\n          );\n      }\n    }\n\n    if (flowMapsEnabled[0]) {\n      float             wheelP  = wheelNP.get_p();\n                        wheelP += -90.0 * delta;\n      if (wheelP > 360) wheelP  =   0;\n      if (wheelP <   0) wheelP  = 360;\n      wheelNP.set_p(wheelP);\n    }\n\n    if (animateSunlight || middayDown || midnightDown) {\n      sunlightP =\n        animateLights\n          ( render\n          , shuttersAnimationCollection\n          , delta\n          , -360.0 / 64.0\n          , closedShutters\n          , middayDown\n          , midnightDown\n          );\n\n      if (middayDown) {\n        statusAlpha = 1.0;\n        statusText = \"Midday\";\n      } else if (midnightDown) {\n        statusAlpha = 1.0;\n        statusText = \"Midnight\";\n      }\n    }\n\n    cameraLookAt =\n      calculateCameraLookAt\n        ( cameraUpDownAdjust\n        , cameraLeftRightAdjust\n        , cameraRotatePhi\n        , cameraRotateTheta\n        , cameraLookAt\n        );\n\n    cameraNP.set_pos\n      ( calculateCameraPosition\n          ( cameraRotateRadius\n          , cameraRotatePhi\n          , cameraRotateTheta\n          , cameraLookAt\n          )\n      );\n\n    cameraNP.look_at(cameraLookAt);\n\n    currentViewWorldMat = cameraNP.get_transform(render)->get_mat();\n\n    geometryNP0.set_shader_input(\"normalMapsEnabled\", normalMapsEnabled);\n    geometryNP0.set_shader_input(\"flowMapsEnabled\",   flowMapsEnabled);\n    geometryCamera0->set_initial_state(geometryNP0.get_state());\n\n    geometryNP1.set_shader_input(\"normalMapsEnabled\", normalMapsEnabled);\n    geometryNP1.set_shader_input(\"flowMapsEnabled\",   flowMapsEnabled);\n    geometryCamera1->set_initial_state(geometryNP1.get_state());\n\n    fogNP.set_shader_input(\"sunPosition\",   LVecBase2f(sunlightP, 0));\n    fogNP.set_shader_input(\"origin\",        cameraNP.get_relative_point(render, environmentNP.get_pos()));\n    fogNP.set_shader_input(\"nearFar\",       LVecBase2f(fogNear, fogFar));\n    fogNP.set_shader_input(\"enabled\",       fogEnabled);\n    fogCamera->set_initial_state(fogNP.get_state());\n\n    ssaoNP.set_shader_input(\"lensProjection\", geometryCameraLens0->get_projection_mat());\n    ssaoNP.set_shader_input(\"enabled\",        ssaoEnabled);\n    ssaoCamera->set_initial_state(ssaoNP.get_state());\n\n    refractionUvNP.set_shader_input(\"lensProjection\", geometryCameraLens1->get_projection_mat());\n    refractionUvNP.set_shader_input(\"enabled\",        refractionEnabled);\n    refractionUvNP.set_shader_input(\"rior\",           rior);\n    refractionUvCamera->set_initial_state(refractionUvNP.get_state());\n\n    reflectionUvNP.set_shader_input(\"lensProjection\", geometryCameraLens1->get_projection_mat());\n    reflectionUvNP.set_shader_input(\"enabled\",        reflectionEnabled);\n    reflectionUvCamera->set_initial_state(reflectionUvNP.get_state());\n\n    foamNP.set_shader_input(\"foamDepth\",    foamDepth);\n    foamNP.set_shader_input(\"viewWorldMat\", currentViewWorldMat);\n    foamNP.set_shader_input(\"sunPosition\",  LVecBase2f(sunlightP, 0));\n    foamCamera->set_initial_state(foamNP.get_state());\n\n    bloomNP.set_shader_input(\"enabled\", bloomEnabled);\n    bloomCamera->set_initial_state(bloomNP.get_state());\n\n    outlineNP.set_shader_input(\"enabled\",             outlineEnabled);\n    outlineCamera->set_initial_state(outlineNP.get_state());\n\n    baseNP.set_shader_input(\"sunPosition\",       LVecBase2f(sunlightP, 0));\n    baseNP.set_shader_input(\"normalMapsEnabled\", normalMapsEnabled);\n    baseNP.set_shader_input(\"blinnPhongEnabled\", blinnPhongEnabled);\n    baseNP.set_shader_input(\"fresnelEnabled\",    fresnelEnabled);\n    baseNP.set_shader_input(\"rimLightEnabled\",   rimLightEnabled);\n    baseNP.set_shader_input(\"celShadingEnabled\", celShadingEnabled);\n    baseNP.set_shader_input(\"flowMapsEnabled\",   flowMapsEnabled);\n    baseCamera->set_initial_state(baseNP.get_state());\n\n    refractionNP.set_shader_input(\"sunPosition\", LVecBase2f(sunlightP, 0));\n    refractionCamera->set_initial_state(refractionNP.get_state());\n\n    sharpenNP.set_shader_input(\"enabled\", sharpenEnabled);\n    sharpenCamera->set_initial_state(sharpenNP.get_state());\n\n    sceneCombineNP.set_shader_input(\"sunPosition\", LVecBase2f(sunlightP, 0));\n    sceneCombineCamera->set_initial_state(sceneCombineNP.get_state());\n\n    depthOfFieldNP.set_shader_input(\"mouseFocusPoint\", mouseFocusPoint);\n    depthOfFieldNP.set_shader_input(\"enabled\",         depthOfFieldEnabled);\n    depthOfFieldCamera->set_initial_state(depthOfFieldNP.get_state());\n\n    painterlyNP.set_shader_input(\"parameters\", LVecBase2f(painterlyEnabled[0] == 1 ? 3 : 0, 0));\n    painterlyCamera->set_initial_state(painterlyNP.get_state());\n\n    motionBlurNP.set_shader_input(\"previousViewWorldMat\",   previousViewWorldMat);\n    motionBlurNP.set_shader_input(\"worldViewMat\",           render.get_transform(cameraNP)->get_mat());\n    motionBlurNP.set_shader_input(\"lensProjection\",         geometryCameraLens1->get_projection_mat());\n    motionBlurNP.set_shader_input(\"motionBlurEnabled\",      motionBlurEnabled);\n    motionBlurCamera->set_initial_state(motionBlurNP.get_state());\n\n    posterizeNP.set_shader_input(\"enabled\", posterizeEnabled);\n    posterizeCamera->set_initial_state(posterizeNP.get_state());\n\n    pixelizeNP.set_shader_input(\"enabled\", pixelizeEnabled);\n    pixelizeCamera->set_initial_state(pixelizeNP.get_state());\n\n    filmGrainNP.set_shader_input(\"enabled\", filmGrainEnabled);\n    filmGrainCamera->set_initial_state(filmGrainNP.get_state());\n\n    lookupTableNP.set_shader_input(\"enabled\",     lookupTableEnabled);\n    lookupTableNP.set_shader_input(\"sunPosition\", LVecBase2f(sunlightP, 0));\n    lookupTableCamera->set_initial_state(lookupTableNP.get_state());\n\n    chromaticAberrationNP.set_shader_input(\"mouseFocusPoint\", mouseFocusPoint);\n    chromaticAberrationNP.set_shader_input(\"enabled\",         chromaticAberrationEnabled);\n    chromaticAberrationCamera->set_initial_state(chromaticAberrationNP.get_state());\n\n    previousViewWorldMat = currentViewWorldMat;\n\n    statusAlpha          = statusAlpha - ((1.0 / statusFadeRate) * delta);\n    statusAlpha          = statusAlpha < 0.0 ? 0.0 : statusAlpha;\n    statusColor[3]       = statusAlpha;\n    statusShadowColor[3] = statusAlpha;\n    status->set_text_color(statusColor);\n    status->set_shadow_color(statusShadowColor);\n    status->set_text(statusText);\n\n    updateAudoManager\n      ( sceneRootNP\n      , cameraNP\n      );\n\n    particleSystemManager.do_particles(delta);\n    physicsManager.do_physics(delta);\n    };\n\n  auto beforeFrameRunner =\n    [](GenericAsyncTask* task, void* arg)\n      -> AsyncTask::DoneStatus {\n          (*static_cast<decltype(beforeFrame)*>(arg))();\n          return AsyncTask::DS_cont;\n      };\n\n  taskManager->add\n    ( new GenericAsyncTask\n        ( \"beforeFrame\"\n        , beforeFrameRunner\n        , &beforeFrame\n        )\n    );\n\n  auto setMouseWheelUp =\n    [&]() {\n    mouseWheelUp = true;\n    };\n\n  auto setMouseWheelDown =\n    [&]() {\n    mouseWheelDown = true;\n    };\n\n  framework.define_key\n    ( \"wheel_up\"\n    , \"Mouse Wheel Up\"\n    , [](const Event*, void* arg) {\n      (*static_cast<decltype(setMouseWheelUp)*>(arg))();\n      }\n    , &setMouseWheelUp\n    );\n\n  framework.define_key\n    ( \"wheel_down\"\n    , \"Mouse Wheel Down\"\n    , [](const Event*, void* arg) {\n      (*static_cast<decltype(setMouseWheelDown)*>(arg))();\n      }\n    , &setMouseWheelDown\n    );\n\n  physicsManager.attach_linear_integrator\n    ( new LinearEulerIntegrator()\n    );\n\n  LVector3f wheelNPRelPos = wheelNP.get_pos(sceneRootNP);\n  sounds[0]->set_3d_attributes\n    ( wheelNPRelPos[0]\n    , wheelNPRelPos[1]\n    , wheelNPRelPos[2]\n    , 0\n    , 0\n    , 0\n    );\n  LVector3f waterNPRelPos = waterNP.get_pos(sceneRootNP);\n  sounds[1]->set_3d_attributes\n    ( waterNPRelPos[0]\n    , waterNPRelPos[1]\n    , waterNPRelPos[2]\n    , 0\n    , 0\n    , 0\n    );\n\n  sounds[0]->set_3d_min_distance(60);\n  sounds[1]->set_3d_min_distance(50);\n\n  framework.main_loop();\n\n  audioManager->shutdown();\n\n  framework.close_framework();\n\n  return 0;\n  }\n\n// END MAIN\n\nvoid generateLights\n  ( NodePath render\n  , bool showLights\n  ) {\n  PT(AmbientLight) ambientLight = new AmbientLight(\"ambientLight\");\n  ambientLight->set_color\n    ( LVecBase4\n        ( 0.388\n        , 0.356\n        , 0.447\n        , 1\n        )\n    );\n  NodePath ambientLightNP = render.attach_new_node(ambientLight);\n  render.set_light(ambientLightNP);\n\n  PT(DirectionalLight) sunlight = new DirectionalLight(\"sunlight\");\n  sunlight->set_color(sunlightColor1);\n  sunlight->set_shadow_caster(true, SHADOW_SIZE, SHADOW_SIZE);\n  sunlight->get_lens()->set_film_size(35, 35);\n  sunlight->get_lens()->set_near_far(5.0, 35.0);\n  if (showLights) sunlight->show_frustum();\n  NodePath sunlightNP = render.attach_new_node(sunlight);\n  sunlightNP.set_name(\"sunlight\");\n  render.set_light(sunlightNP);\n\n  PT(DirectionalLight) moonlight = new DirectionalLight(\"moonlight\");\n  moonlight->set_color(moonlightColor1);\n  moonlight->set_shadow_caster(true, SHADOW_SIZE, SHADOW_SIZE);\n  moonlight->get_lens()->set_film_size(35, 35);\n  moonlight->get_lens()->set_near_far(5.0, 35);\n  if (showLights) moonlight->show_frustum();\n  NodePath moonlightNP = render.attach_new_node(moonlight);\n  moonlightNP.set_name(\"moonlight\");\n  render.set_light_off(moonlightNP);\n\n  NodePath sunlightPivotNP = NodePath(\"sunlightPivot\");\n  sunlightPivotNP.reparent_to(render);\n  sunlightPivotNP.set_pos(0, 0.5, 15.0);\n  sunlightNP.reparent_to(sunlightPivotNP);\n  sunlightNP.set_pos(0, -17.5, 0);\n  sunlightPivotNP.set_hpr(135, 340, 0);\n\n  NodePath moonlightPivotNP = NodePath(\"moonlightPivot\");\n  moonlightPivotNP.reparent_to(render);\n  moonlightPivotNP.set_pos(0, 0.5, 15.0);\n  moonlightNP.reparent_to(moonlightPivotNP);\n  moonlightNP.set_pos(0, -17.5, 0);\n  moonlightPivotNP.set_hpr(135, 160, 0);\n\n  generateWindowLight\n    ( \"windowLight\"\n    , render\n    , LVecBase3\n        ( 1.5\n        , 2.49\n        , 7.9\n        )\n    , showLights\n    );\n  generateWindowLight\n    ( \"windowLight1\"\n    , render\n    , LVecBase3\n        ( 3.5\n        , 2.49\n        , 7.9\n        )\n    , showLights\n    );\n  generateWindowLight\n    ( \"windowLight2\"\n    , render\n    , LVecBase3\n        ( 3.5\n        , 1.49\n        , 4.5\n        )\n    , showLights\n    );\n  }\n\nvoid generateWindowLight\n  ( std::string name\n  , NodePath render\n  , LVecBase3 position\n  , bool show\n  ) {\n  PT(Spotlight) windowLight = new Spotlight(name);\n  windowLight->set_color(windowLightColor);\n  windowLight->set_exponent(5);\n  windowLight->set_attenuation(LVecBase3(1, 0.008, 0));\n  windowLight->set_max_distance(37);\n\n  PT(PerspectiveLens) windowLightLens = new PerspectiveLens();\n  windowLightLens->set_near_far(0.5, 12);\n  windowLightLens->set_fov(140);\n  windowLight->set_lens(windowLightLens);\n\n  if (show) windowLight->show_frustum();\n\n  NodePath windowLightNP = render.attach_new_node(windowLight);\n  windowLightNP.set_name(name);\n  windowLightNP.set_pos(position);\n  windowLightNP.set_hpr(180, 0, 0);\n  render.set_light(windowLightNP);\n  }\n\nfloat animateLights\n  ( NodePath render\n  , AnimControlCollection shuttersAnimationCollection\n  , float delta\n  , float speed\n  , bool& closedShutters\n  , bool  middayDown\n  , bool  midnightDown\n  ) {\n  auto clamp =\n    []\n    ( float a\n    , float mn\n    , float mx\n    ) -> float {\n      if (a > mx) { a = mx; }\n      if (a < mn) { a = mn; }\n      return a;\n    };\n\n  NodePath sunlightPivotNP  = render.find(\"**/sunlightPivot\");\n  NodePath moonlightPivotNP = render.find(\"**/moonlightPivot\");\n  NodePath sunlightNP       = render.find(\"**/sunlight\");\n  NodePath moonlightNP      = render.find(\"**/moonlight\");\n\n  PT(DirectionalLight) sunlight =\n    DCAST(DirectionalLight, sunlightNP.node());\n  PT(DirectionalLight) moonlight =\n    DCAST(DirectionalLight, moonlightNP.node());\n\n  float           p  = sunlightPivotNP.get_p();\n                  p += speed * delta;\n  if (p > 360)    p =   0;\n  if (p < 0)      p = 360;\n\n  if (middayDown) {\n    p = 270;\n  } else if (midnightDown) {\n    p = 90;\n  }\n\n  sunlightPivotNP.set_p( p      );\n  moonlightPivotNP.set_p(p - 180);\n\n  float mixFactor = 1.0 - (sin(toRadians(p)) / 2.0 + 0.5);\n\n  LColor sunlightColor  = mixColor(sunlightColor0,  sunlightColor1, mixFactor);\n  LColor moonlightColor = mixColor(moonlightColor1, sunlightColor0, mixFactor);\n  LColor lightColor     = mixColor(moonlightColor,  sunlightColor,  mixFactor);\n\n  float dayTimeLightMagnitude   = clamp(-1 * sin(toRadians(p)), 0.0, 1.0);\n  float nightTimeLightMagnitude = clamp(     sin(toRadians(p)), 0.0, 1.0);\n\n  sunlight->set_color( lightColor * dayTimeLightMagnitude);\n  moonlight->set_color(lightColor * nightTimeLightMagnitude);\n\n  if (dayTimeLightMagnitude > 0.0) {\n    sunlight->set_shadow_caster(true, SHADOW_SIZE, SHADOW_SIZE);\n    render.set_light(sunlightNP);\n  } else {\n    sunlight->set_shadow_caster(false, 0, 0);\n    render.set_light_off(sunlightNP);\n  }\n\n  if (nightTimeLightMagnitude > 0.0) {\n    moonlight->set_shadow_caster(true, SHADOW_SIZE, SHADOW_SIZE);\n    render.set_light(moonlightNP);\n  } else {\n    moonlight->set_shadow_caster(false, 0, 0);\n    render.set_light_off(moonlightNP);\n  }\n\n  auto updateWindowLight =\n    [&]\n    ( std::string name\n    ) -> void {\n    NodePath windowLightNP = render.find(\"**/\" + name);\n    PT(Spotlight) windowLight = DCAST(Spotlight, windowLightNP.node());\n\n    float windowLightMagnitude = pow(nightTimeLightMagnitude, 0.4);\n\n    windowLight->set_color(windowLightColor * windowLightMagnitude);\n\n    if (windowLightMagnitude <= 0.0) {\n      windowLight->set_shadow_caster(false, 0, 0);\n      render.set_light_off(windowLightNP);\n    } else {\n      windowLight->set_shadow_caster(true, SHADOW_SIZE, SHADOW_SIZE);\n      render.set_light(windowLightNP);\n    }\n    };\n\n  updateWindowLight(\"windowLight\");\n  updateWindowLight(\"windowLight1\");\n  updateWindowLight(\"windowLight2\");\n\n  if (mixFactor >= 0.3 && mixFactor <= 0.35 && closedShutters || midnightDown) {\n    closedShutters = false;\n\n    shuttersAnimationCollection.play(\"open-shutters\");\n  } else if (mixFactor >= 0.6 && mixFactor <= 0.7 && !closedShutters || middayDown) {\n    closedShutters = true;\n\n    shuttersAnimationCollection.play(\"close-shutters\");\n  }\n\n  return p;\n  }\n\nPT(Shader) loadShader\n  ( std::string vert\n  , std::string frag\n  ) {\n  return Shader::load\n    ( Shader::SL_GLSL\n    , \"shaders/vertex/\"   + vert + \".vert\"\n    , \"shaders/fragment/\" + frag + \".frag\"\n    );\n  }\n\nPTA_LVecBase3f generateSsaoSamples\n  ( int numberOfSamples\n  ) {\n  auto lerp = [](float a, float b, float f) -> float {\n    return a + f * (b - a);\n  };\n\n  PTA_LVecBase3f ssaoSamples = PTA_LVecBase3f();\n\n  for (int i = 0; i < numberOfSamples; ++i) {\n    LVecBase3f sample =\n      LVecBase3f\n        ( randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator)\n        ).normalized();\n\n    float rand = randomFloats(generator);\n    sample[0] *= rand;\n    sample[1] *= rand;\n    sample[2] *= rand;\n\n    float scale = (float) i / (float) numberOfSamples;\n    scale = lerp(0.1, 1.0, scale * scale);\n    sample[0] *= scale;\n    sample[1] *= scale;\n    sample[2] *= scale;\n\n    ssaoSamples.push_back(sample);\n  }\n\n  return ssaoSamples;\n  }\n\nPTA_LVecBase3f generateSsaoNoise\n  ( int numberOfNoise\n  ) {\n  PTA_LVecBase3f ssaoNoise = PTA_LVecBase3f();\n\n  for (int i = 0; i < numberOfNoise; ++i) {\n    LVecBase3f noise =\n      LVecBase3f\n        ( randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        , 0.0\n        );\n\n    ssaoNoise.push_back(noise);\n  }\n\n  return ssaoNoise;\n  }\n\nFramebufferTexture generateFramebufferTexture\n  ( FramebufferTextureArguments framebufferTextureArguments\n  ) {\n  PT(WindowFramework)                window         = framebufferTextureArguments.window;\n  PT(GraphicsOutput)                 graphicsOutput = framebufferTextureArguments.graphicsOutput;\n  PT(GraphicsEngine)                 graphicsEngine = framebufferTextureArguments.graphicsEngine;\n  LVecBase4                          rgbaBits       = framebufferTextureArguments.rgbaBits;\n  GraphicsOutput::RenderTexturePlane bitplane       = framebufferTextureArguments.bitplane;\n  int                                aux_rgba       = framebufferTextureArguments.aux_rgba;\n  bool                               setFloatColor  = framebufferTextureArguments.setFloatColor;\n  bool                               setSrgbColor   = framebufferTextureArguments.setSrgbColor;\n  bool                               setRgbColor    = framebufferTextureArguments.setRgbColor;\n  bool                               useScene       = framebufferTextureArguments.useScene;\n  std::string                        name           = framebufferTextureArguments.name;\n  LColor                             clearColor     = framebufferTextureArguments.clearColor;\n\n  FrameBufferProperties fbp = FrameBufferProperties::get_default();\n  fbp.set_back_buffers(0);\n  fbp.set_rgba_bits\n    ( rgbaBits[0]\n    , rgbaBits[1]\n    , rgbaBits[2]\n    , rgbaBits[3]\n    );\n  fbp.set_aux_rgba(aux_rgba);\n  fbp.set_float_color(setFloatColor);\n  fbp.set_srgb_color (setSrgbColor );\n  fbp.set_rgb_color  (setRgbColor  );\n\n  PT(GraphicsOutput) buffer =\n    graphicsEngine\n      ->make_output\n        ( graphicsOutput->get_pipe()\n        , name + \"Buffer\"\n        , BACKGROUND_RENDER_SORT_ORDER - 1\n        , fbp\n        , WindowProperties::size(0, 0),\n            GraphicsPipe::BF_refuse_window\n          | GraphicsPipe::BF_resizeable\n          | GraphicsPipe::BF_can_bind_every\n          | GraphicsPipe::BF_rtt_cumulative\n          | GraphicsPipe::BF_size_track_host\n        , graphicsOutput->get_gsg()\n        , graphicsOutput->get_host()\n        );\n  buffer->add_render_texture\n    ( NULL\n    , GraphicsOutput::RTM_bind_or_copy\n    , bitplane\n    );\n  buffer->set_clear_color(clearColor);\n\n  NodePath   cameraNP = NodePath(\"\");\n  PT(Camera) camera   = NULL;\n\n  if (useScene) {\n    cameraNP = window->make_camera();\n    camera   = DCAST(Camera, cameraNP.node());\n    camera->set_lens(window->get_camera(0)->get_lens());\n  } else {\n    camera = new Camera(name + \"Camera\");\n    PT(OrthographicLens) lens = new OrthographicLens();\n    lens->set_film_size(2, 2);\n    lens->set_film_offset(0, 0);\n    lens->set_near_far(-1, 1);\n    camera->set_lens(lens);\n    cameraNP = NodePath(camera);\n  }\n\n  PT(DisplayRegion) bufferRegion =\n    buffer->make_display_region(0, 1, 0, 1);\n  bufferRegion->set_camera(cameraNP);\n\n  NodePath shaderNP = NodePath(name + \"Shader\");\n\n  if (!useScene) {\n    NodePath renderNP = NodePath(name + \"Render\");\n    renderNP.set_depth_test( false);\n    renderNP.set_depth_write(false);\n    cameraNP.reparent_to(renderNP);\n    CardMaker card = CardMaker(name);\n    card.set_frame_fullscreen_quad();\n    card.set_has_uvs(true);\n    NodePath cardNP = NodePath(card.generate());\n    cardNP.reparent_to(renderNP);\n    cardNP.set_pos(0, 0, 0);\n    cardNP.set_hpr(0, 0, 0);\n    cameraNP.look_at(cardNP);\n  }\n\n  FramebufferTexture result;\n  result.buffer       = buffer;\n  result.bufferRegion = bufferRegion;\n  result.camera       = camera;\n  result.cameraNP     = cameraNP;\n  result.shaderNP     = shaderNP;\n  return result;\n  }\n\nvoid showBuffer\n  ( NodePath render2d\n  , NodePath statusNP\n  , std::tuple<std::string, PT(GraphicsOutput), int> bufferTexture\n  , bool alpha\n  ) {\n  hideBuffer\n    ( render2d\n    );\n  std::string bufferName;\n  PT(GraphicsOutput) buffer;\n  int texture;\n  std::tie(bufferName, buffer, texture) = bufferTexture;\n\n  NodePath nodePath = buffer->get_texture_card();\n  nodePath.set_texture(buffer->get_texture(texture));\n  nodePath.reparent_to(render2d);\n  nodePath.set_y(0);\n\n  if (alpha)\n    nodePath.set_transparency\n      ( TransparencyAttrib::Mode::M_alpha\n      );\n\n  statusNP.reparent_to(nodePath);\n  }\n\nvoid hideBuffer\n  ( NodePath render2d\n  ) {\n  NodePath nodePath = render2d.find(\"**/texture card\");\n  if (nodePath)\n    nodePath.detach_node();\n  }\n\nint microsecondsSinceEpoch\n  (\n  ) {\n  return std::chrono::duration_cast\n    <std::chrono::microseconds>\n      ( std::chrono::system_clock::now().time_since_epoch()\n      ).count();\n  }\n\nbool isButtonDown\n  ( PT(MouseWatcher) mouseWatcher\n  , std::string character\n  ) {\n  return\n    mouseWatcher\n      ->is_button_down\n        ( ButtonRegistry::ptr()->find_button(character)\n        );\n  }\n\nPT(MouseWatcher) getMouseWatcher\n  ( WindowFramework* window\n  ) {\n  return DCAST\n    ( MouseWatcher\n    , window->get_mouse().node()\n    );\n  }\n\nvoid setSoundOff\n  ( PT(AudioSound) sound\n  ) {\n    setSoundState(sound, false);\n  }\n\nvoid setSoundOn\n  ( PT(AudioSound) sound\n  ) {\n    setSoundState(sound, true);\n  }\n\nvoid setSoundState\n  ( PT(AudioSound) sound\n  , bool on\n  ) {\n    if (!on && sound->status() == AudioSound::PLAYING) {\n      sound->stop();\n    } else if (on && sound->status() != AudioSound::PLAYING) {\n      sound->play();\n    }\n  }\n\nvoid updateAudoManager\n  ( NodePath sceneRootNP\n  , NodePath cameraNP\n  ) {\n  LVector3f f = sceneRootNP.get_relative_vector(cameraNP, LVector3f::forward());\n  LVector3f u = sceneRootNP.get_relative_vector(cameraNP, LVector3f::up());\n  LVector3f v = LVector3f(0, 0, 0);\n  LVector3f p = cameraNP.get_pos(sceneRootNP);\n\n  audioManager->audio_3d_set_listener_attributes\n    ( p[0], p[1], p[2]\n    , v[0], v[1], v[2]\n    , f[0], f[1], f[2]\n    , u[0], u[1], u[2]\n    );\n\n  audioManager->update();\n  }\n\nLVecBase3f calculateCameraPosition\n  ( double radius\n  , double phi\n  , double theta\n  , LVecBase3 lookAt\n  ) {\n  double x = radius * sin(toRadians(phi)) * cos(toRadians(theta)) + lookAt[0];\n  double y = radius * sin(toRadians(phi)) * sin(toRadians(theta)) + lookAt[1];\n  double z = radius * cos(toRadians(phi))                         + lookAt[2];\n  return LVecBase3f(x, y, z);\n  }\n\nLVecBase3f calculateCameraLookAt\n  ( double upDownAdjust\n  , double leftRightAdjust\n  , double phi\n  , double theta\n  , LVecBase3 lookAt\n  ) {\n  lookAt[0] +=  upDownAdjust * sin(toRadians(-theta - 90)) * cos(toRadians(phi));\n  lookAt[1] +=  upDownAdjust * cos(toRadians(-theta - 90)) * cos(toRadians(phi));\n  lookAt[2] -= -upDownAdjust * sin(toRadians(phi));\n\n  lookAt[0] += leftRightAdjust * sin(toRadians(-theta));\n  lookAt[1] += leftRightAdjust * cos(toRadians(-theta));\n  return lookAt;\n  }\n\nNodePath setUpParticles\n  ( NodePath render\n  , PT(Texture) smokeTexture\n  ) {\n  PT(ParticleSystem) smokePS = new ParticleSystem();\n  PT(ForceNode)      smokeFN = new ForceNode(\"smoke\");\n  PT(PhysicalNode)   smokePN = new PhysicalNode(\"smoke\");\n\n  smokePS->set_pool_size(75);\n  smokePS->set_birth_rate(0.01);\n  smokePS->set_litter_size(1);\n  smokePS->set_litter_spread(2);\n  smokePS->set_system_lifespan(0.0);\n  smokePS->set_local_velocity_flag(true);\n  smokePS->set_system_grows_older_flag(false);\n\n  PT(PointParticleFactory) smokePPF = new PointParticleFactory();\n  smokePPF->set_lifespan_base(0.1);\n  smokePPF->set_lifespan_spread(3);\n  smokePPF->set_mass_base(1);\n  smokePPF->set_mass_spread(0);\n  smokePPF->set_terminal_velocity_base(400);\n  smokePPF->set_terminal_velocity_spread(0);\n  smokePS->set_factory(smokePPF);\n\n  PT(SpriteParticleRenderer) smokeSPR = new SpriteParticleRenderer();\n  smokeSPR->set_alpha_mode(BaseParticleRenderer::PR_ALPHA_OUT);\n  smokeSPR->set_user_alpha(1.0);\n  smokeSPR->set_texture(smokeTexture);\n  smokeSPR->set_color(LColor(1.0, 1.0, 1.0, 1.0));\n  smokeSPR->set_x_scale_flag(true);\n  smokeSPR->set_y_scale_flag(true);\n  smokeSPR->set_anim_angle_flag(true);\n  smokeSPR->set_initial_x_scale(0.0000001);\n  smokeSPR->set_final_x_scale(  0.007);\n  smokeSPR->set_initial_y_scale(0.0000001);\n  smokeSPR->set_final_y_scale(  0.007);\n  smokeSPR->set_nonanimated_theta(209.0546);\n  smokeSPR->set_alpha_blend_method(BaseParticleRenderer::PP_BLEND_CUBIC);\n  smokeSPR->set_alpha_disable(false);\n  smokeSPR->get_color_interpolation_manager()->add_linear\n    ( 0.0\n    , 1.0\n    , LColor(1.0,   1.0,   1.0,   1.0)\n    , LColor(0.039, 0.078, 0.156, 1.0)\n    , true\n    );\n  smokePS->set_renderer(smokeSPR);\n\n  PT(PointEmitter) smokePE = new PointEmitter();\n  smokePE->set_emission_type(BaseParticleEmitter::ET_EXPLICIT);\n  smokePE->set_amplitude(0.0);\n  smokePE->set_amplitude_spread(1.0);\n  smokePE->set_offset_force(LVector3f(0.0, 0.0, 2.0));\n  smokePE->set_explicit_launch_vector(LVector3f(0.0, 0.1, 0.0));\n  smokePE->set_radiate_origin(LPoint3f(0.0, 0.0, 0.0));\n  smokePE->set_location(LPoint3f(0.0, 0.0, 0.0));\n  smokePS->set_emitter(smokePE);\n\n  PT(LinearVectorForce) smokeLVF = new LinearVectorForce(LVector3f(3.0, -2.0, 0.0), 1.0, false);\n  smokeLVF->set_vector_masks(true, true, true);\n  smokeLVF->set_active(true);\n  smokeFN->add_force(smokeLVF);\n  smokePS->add_linear_force(smokeLVF);\n\n  PT(LinearJitterForce) smokeLJF = new LinearJitterForce(2.0, false);\n  smokeLJF->set_vector_masks(true, true, true);\n  smokeLJF->set_active(true);\n  smokeFN->add_force(smokeLJF);\n  smokePS->add_linear_force(smokeLJF);\n\n  PT(LinearCylinderVortexForce) smokeLCVF = new LinearCylinderVortexForce(10.0, 1.0, 4.0, 1.0, false);\n  smokeLCVF->set_vector_masks(true, true, true);\n  smokeLCVF->set_active(true);\n  smokeFN->add_force(smokeLCVF);\n  smokePS->add_linear_force(smokeLCVF);\n\n  smokePN->insert_physical(0, smokePS);\n  smokePS->set_render_parent(smokePN);\n  NodePath smokeNP = render.attach_new_node(smokePN);\n  smokeNP.attach_new_node(smokeFN);\n\n  particleSystemManager.attach_particlesystem(smokePS);\n  physicsManager.attach_physical(smokePS);\n\n  smokeNP.set_pos(0.47, 4.5, 8.9);\n  smokeNP.set_transparency(TransparencyAttrib::M_dual);\n  smokeNP.set_bin(\"fixed\", 0);\n\n  return smokeNP;\n  }\n\nvoid squashGeometry\n  ( NodePath environmentNP\n  ) {\n  for (int i = 0; i < 4; ++i) {\n    std::string index = std::to_string(i);\n    NodePathCollection treeCollection = environmentNP.find_all_matches(\"**/tree\" + index);\n    NodePath treesNP = NodePath(\"treeCollection\" + index);\n    treesNP.reparent_to(environmentNP);\n    treeCollection.reparent_to(treesNP);\n    treesNP.flatten_strong();\n  }\n\n  NodePathCollection barrelCollection = environmentNP.find_all_matches(\"**/barrel-wood*\");\n  NodePath barrelNP = NodePath(\"barrels\");\n  barrelNP.reparent_to(environmentNP);\n  barrelCollection.reparent_to(barrelNP);\n  barrelNP.flatten_strong();\n\n  NodePath squashNP = NodePath(\"squash\");\n  squashNP.reparent_to(environmentNP);\n\n  NodePathCollection squashCollection = environmentNP.find_all_matches(\"**/*\");\n  for (int i = 0; i < squashCollection.size(); ++i) {\n    if  (   squashCollection[i].get_name() == \"wheel-lp\"\n        ||  squashCollection[i].get_name() == \"water-lp\"\n        ||  squashCollection[i].get_name() == \"squash\"\n        ) { continue; }\n    squashCollection[i].reparent_to(squashNP);\n  }\n  squashNP.flatten_strong();\n  }\n\ndouble microsecondToSecond\n  ( int m\n  ) {\n  return m / 1000000.0;\n  }\n\ndouble toRadians\n  ( double d\n  ) {\n  return d * M_PI / 180.0;\n  }\n\nLVecBase2f makeEnabledVec\n  ( int t\n  ) {\n  if (t >= 1) { t = 1; } else { t = 0; }\n  return LVecBase2f(t, t);\n  }\n\nLVecBase2f toggleEnabledVec\n  ( LVecBase2f vec\n  ) {\n  int t = vec[0];\n  if (t >= 1) { t = 0; } else { t = 1; }\n  vec[0] = t;\n  vec[1] = t;\n  return vec;\n  }\n\nvoid setTextureToNearestAndClamp\n  ( PT(Texture) texture\n  ) {\n    texture->set_magfilter(SamplerState::FT_nearest);\n    texture->set_minfilter(SamplerState::FT_nearest);\n    texture->set_wrap_u(SamplerState::WM_clamp);\n    texture->set_wrap_v(SamplerState::WM_clamp);\n    texture->set_wrap_w(SamplerState::WM_clamp);\n  }\n\nLColor mixColor\n  ( LColor a\n  , LColor b\n  , float factor\n  ) {\n    return a * (1 - factor) + b * factor;\n  }\n"
  },
  {
    "path": "docs/_build-docs.sh",
    "content": "#!/usr/bin/env bash\n\nSCRIPT_PATH=\"$(cd \"$(dirname \"$0\")\"; pwd -P)\"\nMAIN_TITLE=\"3D Game Shaders For Beginners\"\nREPO_URL=\"https://github.com/lettier/3d-game-shaders-for-beginners\"\nAUTHOR=\"David Lettier\"\nCSS=\"style.css\"\n\nfor f in $SCRIPT_PATH/../sections/*\ndo\n  echo \"$f\"\n  file=$(basename -- \"$f\")\n  file_name=\"${file%.*}\"\n  title=$(echo \"$file_name\" | sed -r 's/-/ /g' | sed -e 's/\\b\\(.\\)/\\u\\1/g')\n  if [ \"$title\" == \"Ssao\" ]\n  then\n    title=\"SSAO\"\n  fi\n  if [ \"$title\" == \"Glsl\" ]\n  then\n    title=\"GLSL\"\n  fi\n  $PANDOC \\\n    -f gfm \\\n    -t html5 \\\n    --highlight-style=breezedark \\\n    --template=$SCRIPT_PATH/_template.html5 \\\n    $f \\\n    --metadata pagetitle=\"$title | $MAIN_TITLE\" \\\n    --metadata author-meta=\"$AUTHOR\" \\\n    --metadata css=$CSS \\\n    -o \"$SCRIPT_PATH/$file_name.html\"\ndone\n\n$PANDOC \\\n  -f gfm \\\n  -t html5 \\\n  --highlight-style=breezedark \\\n  --template=$SCRIPT_PATH/_template.html5 \\\n  $SCRIPT_PATH/../README.md \\\n  --metadata pagetitle=\"$MAIN_TITLE\" \\\n  --metadata author-meta=\"$AUTHOR\" \\\n  --metadata css=$CSS \\\n  -o \"$SCRIPT_PATH/index.html\"\n\nfor i in {1..3}\ndo\n  for f in $SCRIPT_PATH/*\n  do\n    file=$(basename -- \"$f\")\n    file_name=\"${file%.*}\"\n    file_ext=\"${file##*.}\"\n    if [ \"$file_ext\" == \"html\" ]\n    then\n      echo $f\n      sed -i -E 's/href=\"(sections\\/)?([a-z-]+)\\.md(.*)\"/href=\"\\2\\.html\\3\"/g' $f\n      sed -i -E 's/href=\"\\.\\.\\/README.md\"/href=\"index.html\"/g' $f\n      sed -i -E 's+<a href=\"\\.\\.\\/demonstration\\/(.*)\">+<a href=\"'$REPO_URL'\\/blob\\/master\\/demonstration\\/\\1\" target=\"_blank\" rel=\"noopener noreferrer\">+g' $f\n    fi\n  done\ndone\n"
  },
  {
    "path": "docs/_template.html5",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"$lang$\" xml:lang=\"$lang$\"$if(dir)$ dir=\"$dir$\"$endif$>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n$for(author-meta)$\n    <meta name=\"author\" content=\"$author-meta$\" />\n$endfor$\n$if(date-meta)$\n    <meta name=\"dcterms.date\" content=\"$date-meta$\" />\n$endif$\n$if(keywords)$\n    <meta name=\"keywords\" content=\"$for(keywords)$$keywords$$sep$, $endfor$\" />\n$endif$\n    <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n$if(quotes)$\n      q { quotes: \"“\" \"”\" \"‘\" \"’\"; }\n$endif$\n    </style>\n$if(highlighting-css)$\n    <style>\n      $highlighting-css$\n    </style>\n$endif$\n$if(math)$\n  $math$\n$endif$\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n$for(header-includes)$\n  $header-includes$\n$endfor$\n$for(css)$\n    <link rel=\"stylesheet\" href=\"$css$\" />\n$endfor$\n  </head>\n  <body>\n$for(include-before)$\n    $include-before$\n$endfor$\n$if(title)$\n    <header id=\"title-block-header\">\n    <h1 class=\"title\">$title$</h1>\n$if(subtitle)$\n    <p class=\"subtitle\">$subtitle$</p>\n$endif$\n$for(author)$\n    <p class=\"author\">$author$</p>\n$endfor$\n$if(date)$\n    <p class=\"date\">$date$</p>\n$endif$\n    </header>\n$endif$\n$if(toc)$\n    <nav id=\"$idprefix$TOC\" role=\"doc-toc\">\n$table-of-contents$\n    </nav>\n$endif$\n$body$\n$for(include-after)$\n    $include-after$\n$endfor$\n  </body>\n</html>\n"
  },
  {
    "path": "docs/blinn-phong.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Blinn Phong | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Blinn Phong | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Blinn Phong | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"fresnel-factor.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"blinn-phong\">Blinn-Phong</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/CFWEeGK.gif\" alt=\"Blinn-Phong\" title=\"Blinn-Phong\">\n</p>\n\n<p>Blinn-Phong is a slight adjustment of the Phong model you saw in the <a href=\"lighting.html\">lighting</a> section. It provides more plausible or realistic specular reflections. You'll notice that Blinn-Phong produces elliptical or elongated specular reflections versus the spherical specular reflections produced by the Phong model. In certain cases, Blinn-Phong can be more efficient to calculate than Phong.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  vec3 light   = normal(lightPosition.xyz - vertexPosition.xyz);</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  vec3 eye     = normalize(-vertexPosition.xyz);</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  vec3 halfway = normalize(light + eye);</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Instead of computing the reflection vector, compute the halfway or half angle vector. This vector is between the view/camera/eye and light direction vector.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/vtqd1Ox.gif\" alt=\"Blinn-Phong vs Phong\" title=\"Blinn-Phong vs Phong\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>    <span class=\"dt\">float</span> specularIntensity = dot(normal, halfway);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The specular intensity is now the dot product of the normal and halfway vector. In the Phong model, it is the dot product of the reflection and view vector.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WZQqxEH.png\" alt=\"Full specular intensity.\" title=\"Full specular intensity.\">\n</p>\n\n<p>The half angle vector (magenta arrow) will point in the same direction as the normal (green arrow) when the view vector (orange arrow) points in the same direction as the reflection vector (magenta arrow). In this case, both the Blinn-Phong and Phong specular intensity will be one.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/kiSdJzt.png\" alt=\"Blinn-Phong vs Phong\" title=\"Blinn-Phong vs Phong\">\n</p>\n\n<p>In other cases, the specular intensity for Blinn-Phong will be greater than zero while the specular intensity for Phong will be zero.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"fresnel-factor.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/bloom.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Bloom | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Bloom | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Bloom | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"blur.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"ssao.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"bloom\">Bloom</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/UxKRz2r.gif\" alt=\"Bloom\" title=\"Bloom\">\n</p>\n\n<p>Adding bloom to a scene can really sell the illusion of the lighting model. Light emitting objects are more believable and specular highlights get an extra dose of shimmer.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">//...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">int</span>   size       = <span class=\"dv\">5</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  <span class=\"dt\">float</span> separation = <span class=\"dv\">3</span>;</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"dt\">float</span> threshold  = <span class=\"fl\">0.4</span>;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>  <span class=\"dt\">float</span> amount     = <span class=\"dv\">1</span>;</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a></span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>These parameters control the look and feel. <code>size</code> determines how blurred the effect is. <code>separation</code> spreads out the blur. <code>threshold</code> controls which fragments are illuminated. And the last parameter, <code>amount</code>, controls how much bloom is outputted.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  vec2 texSize = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"dt\">float</span> value = <span class=\"fl\">0.0</span>;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"dt\">float</span> count = <span class=\"fl\">0.0</span>;</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a></span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  vec4 result = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>  vec4 color  = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a></span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = -size; i &lt;= size; ++i) {</span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a>    <span class=\"cf\">for</span> (<span class=\"dt\">int</span> j = -size; j &lt;= size; ++j) {</span>\n<span id=\"cb2-13\"><a href=\"#cb2-13\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb2-14\"><a href=\"#cb2-14\"></a>    }</span>\n<span id=\"cb2-15\"><a href=\"#cb2-15\"></a>  }</span>\n<span id=\"cb2-16\"><a href=\"#cb2-16\"></a></span>\n<span id=\"cb2-17\"><a href=\"#cb2-17\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The technique starts by looping through a kernel/matrix/window centered over the current fragment. This is similar to the window used for <a href=\"outlining.html\">outlining</a>. The size of the window is <code>size * 2 + 1</code> by <code>size * 2 + 1</code>. So for example, with a <code>size</code> setting of two, the window uses <code>(2 * 2 + 1)^2 = 25</code> samples per fragment.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>      color =</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>        texture</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>          ( colorTexture</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>          ,   ( gl_FragCoord.xy</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>              + (vec2(i, j) * separation)</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>              )</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>            / texSize</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>          );</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>      value = max(color.r, max(color.g, color.b));</span>\n<span id=\"cb3-13\"><a href=\"#cb3-13\"></a>      <span class=\"cf\">if</span> (value &lt; threshold) { color = vec4(<span class=\"dv\">0</span>); }</span>\n<span id=\"cb3-14\"><a href=\"#cb3-14\"></a></span>\n<span id=\"cb3-15\"><a href=\"#cb3-15\"></a>      result += color;</span>\n<span id=\"cb3-16\"><a href=\"#cb3-16\"></a>      count  += <span class=\"fl\">1.0</span>;</span>\n<span id=\"cb3-17\"><a href=\"#cb3-17\"></a></span>\n<span id=\"cb3-18\"><a href=\"#cb3-18\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>For each iteration, it retrieves the color from the input texture and turns the red, green, and blue values into a greyscale value. If this greyscale value is less than the threshold, it discards this color by making it solid black. After evaluating the sample's greyscale value, it adds its RGB values to <code>result</code>.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  result /= count;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  fragColor = mix(vec4(<span class=\"dv\">0</span>), result, amount);</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>After it's done summing up the samples, it divides the sum of the color samples by the number of samples taken. The result is the average color of itself and its neighbors. By doing this for every fragment, you end up with a blurred image. This form of blurring is known as a <a href=\"blur.html#box-blur\">box blur</a>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/m4yedrM.gif\" alt=\"Bloom progresssion.\" title=\"Bloom progresssion.\">\n</p>\n\n<p>Here you see the progression of the bloom algorithm.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/outline.frag\" target=\"_blank\" rel=\"noopener noreferrer\">bloom.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"blur.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"ssao.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/blur.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Blur | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Blur | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Blur | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"fog.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"bloom.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"blur\">Blur</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b5vw2AJ.gif\" alt=\"Kuwahara Filter\" title=\"Kuwahara Filter\">\n</p>\n\n<p>The need to blur this or that can come up quite often as you try to obtain a particular look or perform some technique like motion blur. Below are just some of ways you can blur your game's imagery.</p>\n<h3 id=\"box-blur\">Box Blur</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uaXC1JM.gif\" alt=\"Box Blur\" title=\"Box Blur\">\n</p>\n\n<p>The box blur or mean filter algorithm is a simple to implement blurring effect. It's fast and gets the job done. If you need more finesse, you can upgrade to a Gaussian blur.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a></span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>  <span class=\"dt\">int</span> size  = <span class=\"dt\">int</span>(parameters.x);</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>  <span class=\"cf\">if</span> (size &lt;= <span class=\"dv\">0</span>) { fragColor = texture(colorTexture, texCoord); <span class=\"cf\">return</span>; }</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a></span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>size</code> parameter controls how blurry the result is. If the <code>size</code> is zero or less, return the fragment untouched.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> separation = parameters.y;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>        separation = max(separation, <span class=\"dv\">1</span>);</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>separation</code> parameter spreads out the blur without having to sample additional fragments. <code>separation</code> ranges from one to infinity.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bRw0OkX.png\" alt=\"Blur Kernel\" title=\"Blur Kernel\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = -size; i &lt;= size; ++i) {</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>    <span class=\"cf\">for</span> (<span class=\"dt\">int</span> j = -size; j &lt;= size; ++j) {</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>    }</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  }</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a></span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like the <a href=\"outlining.html\">outlining</a> technique, the box blur technique uses a kernel/matrix/window centered around the current fragment. The size of the window is <code>size * 2 + 1</code> by <code>size * 2 + 1</code>. So for example, with a <code>size</code> setting of two, the window uses <code>(2 * 2 + 1)^2 = 25</code> samples per fragment.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>      fragColor +=</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>        texture</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>          ( colorTexture</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>          ,   ( gl_FragCoord.xy</span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>              + (vec2(i, j) * separation)</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>              )</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>            / texSize</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>          );</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a></span>\n<span id=\"cb4-12\"><a href=\"#cb4-12\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To compute the mean or average of the samples in the window, start by loop through the window, adding up each color vector.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  fragColor /= pow(size * <span class=\"dv\">2</span> + <span class=\"dv\">1</span>, <span class=\"dv\">2</span>);</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To finish computing the mean, divide the sum of the colors sampled by the number of samples taken. The final fragment color is the mean or average of the fragments sampled inside the window.</p>\n<h3 id=\"median-filter\">Median Filter</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/T1nEEn3.gif\" alt=\"Median Filter\" title=\"Median Filter\">\n</p>\n\n<p>The box blur uses the mean color of the samples taken. The median filter uses the median color of the samples taken. By using the median instead of the mean, the edges in the image are preserved—meaning the edges stay nice and crisp. For example, look at the windows in the box blurred image versus the median filtered image.</p>\n<p>Unfortunately, finding the median can be slower than finding the mean. You could sort the values and choose the middle one but that would take at least quasilinear time. There is a technique to find the median in linear time but it can be quite awkward inside a shader. The numerical approach below approximates the median in linear time. How well it approximates the median can be controlled.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AnbzUmN.png\" alt=\"Painterly\" title=\"Painterly\">\n</p>\n\n<p>At lower quality approximations, you end up with a nice <a href=\"https://en.wikipedia.org/wiki/Painterliness\">painterly</a> look.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a><span class=\"pp\">#define MAX_SIZE        4</span></span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a><span class=\"pp\">#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))</span></span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a><span class=\"pp\">#define MAX_BINS_SIZE   100</span></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a></span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>These are the hard limits for the <code>size</code> parameter, window size, and <code>bins</code> array.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a></span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>  <span class=\"dt\">int</span> size = <span class=\"dt\">int</span>(parameters.x);</span>\n<span id=\"cb7-7\"><a href=\"#cb7-7\"></a>  <span class=\"cf\">if</span> (size &lt;= <span class=\"dv\">0</span>) { fragColor = texture(colorTexture, texCoord); <span class=\"cf\">return</span>; }</span>\n<span id=\"cb7-8\"><a href=\"#cb7-8\"></a>  <span class=\"cf\">if</span> (size &gt; MAX_SIZE) { size = MAX_SIZE; }</span>\n<span id=\"cb7-9\"><a href=\"#cb7-9\"></a>  <span class=\"dt\">int</span> kernelSize = <span class=\"dt\">int</span>(pow(size * <span class=\"dv\">2</span> + <span class=\"dv\">1</span>, <span class=\"dv\">2</span>));</span>\n<span id=\"cb7-10\"><a href=\"#cb7-10\"></a></span>\n<span id=\"cb7-11\"><a href=\"#cb7-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>size</code> parameter controls how blurry or smeared the effect is. If the size is at or below zero, return the current fragment untouched. From the <code>size</code> parameter, calculate the total size of the kernel or window. This is how many samples you'll be taking per fragment.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  <span class=\"dt\">int</span> binsSize = <span class=\"dt\">int</span>(parameters.y);</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>      binsSize = clamp(binsSize, <span class=\"dv\">1</span>, MAX_BINS_SIZE);</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a></span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Set up the <code>binsSize</code>, making sure to limit it by the <code>MAX_BINS_SIZE</code>.</p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  <span class=\"dt\">int</span> i        = <span class=\"dv\">0</span>;</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a>  <span class=\"dt\">int</span> j        = <span class=\"dv\">0</span>;</span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>  <span class=\"dt\">int</span> count    = <span class=\"dv\">0</span>;</span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\"></a>  <span class=\"dt\">int</span> binIndex = <span class=\"dv\">0</span>;</span>\n<span id=\"cb9-7\"><a href=\"#cb9-7\"></a></span>\n<span id=\"cb9-8\"><a href=\"#cb9-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>i</code> and <code>j</code> are used to sample the given texture around the current fragment. <code>i</code> is also used as a general for loop count. <code>count</code> is used in the initialization of the <code>colors</code> array which you'll see later. <code>binIndex</code> is used to approximate the median color.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  vec4  colors[MAX_KERNEL_SIZE];</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a>  <span class=\"dt\">float</span> bins[MAX_BINS_SIZE];</span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>  <span class=\"dt\">int</span>   binIndexes[colors.length()];</span>\n<span id=\"cb10-6\"><a href=\"#cb10-6\"></a></span>\n<span id=\"cb10-7\"><a href=\"#cb10-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>colors</code> array holds the sampled colors taken from the input texture. <code>bins</code> is used to approximate the median of the sampled colors. Each bin holds a count of how many colors fall into its range when converting each color into a greyscale value (between zero and one). As <code>binsSize</code> approaches 100, the algorithm finds the true median almost always. <code>binIndexes</code> stores the <code>bins</code> index or which bin each sample falls into.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>  <span class=\"dt\">float</span> total = <span class=\"dv\">0</span>;</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a>  <span class=\"dt\">float</span> limit = floor(<span class=\"dt\">float</span>(kernelSize) / <span class=\"dv\">2</span>) + <span class=\"dv\">1</span>;</span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a></span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>total</code> keeps track of how many colors you've come across as you loop through <code>bins</code>. When <code>total</code> reaches <code>limit</code>, you return whatever <code>bins</code> index you're at. The <code>limit</code> is the median index. For example, if the window size is 81, <code>limit</code> is 41 which is directly in the middle (40 samples below and 40 samples above).</p>\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>  <span class=\"dt\">float</span> value       = <span class=\"dv\">0</span>;</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a>  vec3  valueRatios = vec3(<span class=\"fl\">0.3</span>, <span class=\"fl\">0.59</span>, <span class=\"fl\">0.11</span>);</span>\n<span id=\"cb12-5\"><a href=\"#cb12-5\"></a></span>\n<span id=\"cb12-6\"><a href=\"#cb12-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>These are used to covert and hold each color sample's greyscale value. Instead of dividing red, green, and blue by one third, it uses 30% of red, 59% of green, and 11% of blue for a total of 100%.</p>\n<div class=\"sourceCode\" id=\"cb13\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb13-2\"><a href=\"#cb13-2\"></a></span>\n<span id=\"cb13-3\"><a href=\"#cb13-3\"></a>  <span class=\"cf\">for</span> (i = -size; i &lt;= size; ++i) {</span>\n<span id=\"cb13-4\"><a href=\"#cb13-4\"></a>    <span class=\"cf\">for</span> (j = -size; j &lt;= size; ++j) {</span>\n<span id=\"cb13-5\"><a href=\"#cb13-5\"></a>      colors[count] =</span>\n<span id=\"cb13-6\"><a href=\"#cb13-6\"></a>        texture</span>\n<span id=\"cb13-7\"><a href=\"#cb13-7\"></a>          ( colorTexture</span>\n<span id=\"cb13-8\"><a href=\"#cb13-8\"></a>          ,   ( gl_FragCoord.xy</span>\n<span id=\"cb13-9\"><a href=\"#cb13-9\"></a>              + vec2(i, j)</span>\n<span id=\"cb13-10\"><a href=\"#cb13-10\"></a>              )</span>\n<span id=\"cb13-11\"><a href=\"#cb13-11\"></a>            / texSize</span>\n<span id=\"cb13-12\"><a href=\"#cb13-12\"></a>          );</span>\n<span id=\"cb13-13\"><a href=\"#cb13-13\"></a>      count += <span class=\"dv\">1</span>;</span>\n<span id=\"cb13-14\"><a href=\"#cb13-14\"></a>    }</span>\n<span id=\"cb13-15\"><a href=\"#cb13-15\"></a>  }</span>\n<span id=\"cb13-16\"><a href=\"#cb13-16\"></a></span>\n<span id=\"cb13-17\"><a href=\"#cb13-17\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Loop through the window and collect the color samples into <code>colors</code>.</p>\n<div class=\"sourceCode\" id=\"cb14\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb14-1\"><a href=\"#cb14-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb14-2\"><a href=\"#cb14-2\"></a></span>\n<span id=\"cb14-3\"><a href=\"#cb14-3\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; binsSize; ++i) {</span>\n<span id=\"cb14-4\"><a href=\"#cb14-4\"></a>    bins[i] = <span class=\"dv\">0</span>;</span>\n<span id=\"cb14-5\"><a href=\"#cb14-5\"></a>  }</span>\n<span id=\"cb14-6\"><a href=\"#cb14-6\"></a></span>\n<span id=\"cb14-7\"><a href=\"#cb14-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Initialize the <code>bins</code> array with zeros.</p>\n<div class=\"sourceCode\" id=\"cb15\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb15-1\"><a href=\"#cb15-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb15-2\"><a href=\"#cb15-2\"></a></span>\n<span id=\"cb15-3\"><a href=\"#cb15-3\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; kernelSize; ++i) {</span>\n<span id=\"cb15-4\"><a href=\"#cb15-4\"></a>    value           = dot(colors[i].rgb, valueRatios);</span>\n<span id=\"cb15-5\"><a href=\"#cb15-5\"></a>    binIndex        = <span class=\"dt\">int</span>(floor(value * binsSize));</span>\n<span id=\"cb15-6\"><a href=\"#cb15-6\"></a>    binIndex        = clamp(binIndex, <span class=\"dv\">0</span>, binsSize - <span class=\"dv\">1</span>);</span>\n<span id=\"cb15-7\"><a href=\"#cb15-7\"></a>    bins[binIndex] += <span class=\"dv\">1</span>;</span>\n<span id=\"cb15-8\"><a href=\"#cb15-8\"></a>    binIndexes[i]   = binIndex;</span>\n<span id=\"cb15-9\"><a href=\"#cb15-9\"></a>  }</span>\n<span id=\"cb15-10\"><a href=\"#cb15-10\"></a></span>\n<span id=\"cb15-11\"><a href=\"#cb15-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Loop through the colors and convert each one to a greyscale value. <code>dot(colors[i].rgb, valueRatios)</code> is the weighted sum <code>colors.r * 0.3 + colors.g * 0.59 + colors.b * 0.11</code>.</p>\n<p>Each value will fall into some bin. Each bin covers some range of values. For example, if the number of bins is 10, the first bin covers everything from zero up to but not including 0.1. Increment the number of colors that fall into this bin and remember the color sample's bin index so you can look it up later.</p>\n<div class=\"sourceCode\" id=\"cb16\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb16-1\"><a href=\"#cb16-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb16-2\"><a href=\"#cb16-2\"></a></span>\n<span id=\"cb16-3\"><a href=\"#cb16-3\"></a>  binIndex = <span class=\"dv\">0</span>;</span>\n<span id=\"cb16-4\"><a href=\"#cb16-4\"></a></span>\n<span id=\"cb16-5\"><a href=\"#cb16-5\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; binsSize; ++i) {</span>\n<span id=\"cb16-6\"><a href=\"#cb16-6\"></a>    total += bins[i];</span>\n<span id=\"cb16-7\"><a href=\"#cb16-7\"></a>    <span class=\"cf\">if</span> (total &gt;= limit) {</span>\n<span id=\"cb16-8\"><a href=\"#cb16-8\"></a>      binIndex = i;</span>\n<span id=\"cb16-9\"><a href=\"#cb16-9\"></a>      <span class=\"cf\">break</span>;</span>\n<span id=\"cb16-10\"><a href=\"#cb16-10\"></a>    }</span>\n<span id=\"cb16-11\"><a href=\"#cb16-11\"></a>  }</span>\n<span id=\"cb16-12\"><a href=\"#cb16-12\"></a></span>\n<span id=\"cb16-13\"><a href=\"#cb16-13\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Loop through the bins, tallying up the number of colors seen so far. When you reach the median index, exit the loop and remember the last <code>bins</code> index reached.</p>\n<div class=\"sourceCode\" id=\"cb17\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb17-1\"><a href=\"#cb17-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb17-2\"><a href=\"#cb17-2\"></a></span>\n<span id=\"cb17-3\"><a href=\"#cb17-3\"></a>  fragColor = colors[<span class=\"dv\">0</span>];</span>\n<span id=\"cb17-4\"><a href=\"#cb17-4\"></a></span>\n<span id=\"cb17-5\"><a href=\"#cb17-5\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; kernelSize; ++i) {</span>\n<span id=\"cb17-6\"><a href=\"#cb17-6\"></a>    <span class=\"cf\">if</span> (binIndexes[i] == binIndex) {</span>\n<span id=\"cb17-7\"><a href=\"#cb17-7\"></a>      fragColor = colors[i];</span>\n<span id=\"cb17-8\"><a href=\"#cb17-8\"></a>      <span class=\"cf\">break</span>;</span>\n<span id=\"cb17-9\"><a href=\"#cb17-9\"></a>    }</span>\n<span id=\"cb17-10\"><a href=\"#cb17-10\"></a>  }</span>\n<span id=\"cb17-11\"><a href=\"#cb17-11\"></a></span>\n<span id=\"cb17-12\"><a href=\"#cb17-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now loop through the <code>binIndexes</code> and find the first color with the last <code>bins</code> indexed reached. Its greyscale value is the approximated median which in many cases will be the true median value. Set this color as the fragColor and exit the loop and shader.</p>\n<h3 id=\"kuwahara-filter\">Kuwahara Filter</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b5vw2AJ.gif\" alt=\"Kuwahara Filter\" title=\"Kuwahara Filter\">\n</p>\n\n<p>Like the median filter, the kuwahara filter preserves the major edges found in the image. You'll notice that it has a more block like or chunky pattern to it. In practice, the Kuwahara filter runs faster than the median filter, allowing for larger <code>size</code> values without a noticeable slowdown.</p>\n<div class=\"sourceCode\" id=\"cb18\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb18-1\"><a href=\"#cb18-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb18-2\"><a href=\"#cb18-2\"></a></span>\n<span id=\"cb18-3\"><a href=\"#cb18-3\"></a><span class=\"pp\">#define MAX_SIZE        5</span></span>\n<span id=\"cb18-4\"><a href=\"#cb18-4\"></a><span class=\"pp\">#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))</span></span>\n<span id=\"cb18-5\"><a href=\"#cb18-5\"></a></span>\n<span id=\"cb18-6\"><a href=\"#cb18-6\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Set a hard limit for the <code>size</code> parameter and the number of samples taken.</p>\n<div class=\"sourceCode\" id=\"cb19\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb19-1\"><a href=\"#cb19-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb19-2\"><a href=\"#cb19-2\"></a></span>\n<span id=\"cb19-3\"><a href=\"#cb19-3\"></a><span class=\"dt\">int</span> i     = <span class=\"dv\">0</span>;</span>\n<span id=\"cb19-4\"><a href=\"#cb19-4\"></a><span class=\"dt\">int</span> j     = <span class=\"dv\">0</span>;</span>\n<span id=\"cb19-5\"><a href=\"#cb19-5\"></a><span class=\"dt\">int</span> count = <span class=\"dv\">0</span>;</span>\n<span id=\"cb19-6\"><a href=\"#cb19-6\"></a></span>\n<span id=\"cb19-7\"><a href=\"#cb19-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>These are used to sample the input texture and set up the <code>values</code> array.</p>\n<div class=\"sourceCode\" id=\"cb20\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb20-1\"><a href=\"#cb20-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb20-2\"><a href=\"#cb20-2\"></a></span>\n<span id=\"cb20-3\"><a href=\"#cb20-3\"></a>vec3  valueRatios = vec3(<span class=\"fl\">0.3</span>, <span class=\"fl\">0.59</span>, <span class=\"fl\">0.11</span>);</span>\n<span id=\"cb20-4\"><a href=\"#cb20-4\"></a></span>\n<span id=\"cb20-5\"><a href=\"#cb20-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like the median filter, you'll be converting the color samples into greyscale values.</p>\n<div class=\"sourceCode\" id=\"cb21\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb21-1\"><a href=\"#cb21-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb21-2\"><a href=\"#cb21-2\"></a></span>\n<span id=\"cb21-3\"><a href=\"#cb21-3\"></a><span class=\"dt\">float</span> values[MAX_KERNEL_SIZE];</span>\n<span id=\"cb21-4\"><a href=\"#cb21-4\"></a></span>\n<span id=\"cb21-5\"><a href=\"#cb21-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Initialize the <code>values</code> array. This will hold the greyscale values for the color samples.</p>\n<div class=\"sourceCode\" id=\"cb22\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb22-1\"><a href=\"#cb22-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb22-2\"><a href=\"#cb22-2\"></a></span>\n<span id=\"cb22-3\"><a href=\"#cb22-3\"></a>vec4  color       = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb22-4\"><a href=\"#cb22-4\"></a>vec4  meanTemp    = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb22-5\"><a href=\"#cb22-5\"></a>vec4  mean        = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb22-6\"><a href=\"#cb22-6\"></a><span class=\"dt\">float</span> valueMean   = <span class=\"dv\">0</span>;</span>\n<span id=\"cb22-7\"><a href=\"#cb22-7\"></a><span class=\"dt\">float</span> variance    = <span class=\"dv\">0</span>;</span>\n<span id=\"cb22-8\"><a href=\"#cb22-8\"></a><span class=\"dt\">float</span> minVariance = -<span class=\"dv\">1</span>;</span>\n<span id=\"cb22-9\"><a href=\"#cb22-9\"></a></span>\n<span id=\"cb22-10\"><a href=\"#cb22-10\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The Kuwahara filter works by computing the variance of four subwindows and then using the mean of the subwindow with the smallest variance.</p>\n<div class=\"sourceCode\" id=\"cb23\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb23-1\"><a href=\"#cb23-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb23-2\"><a href=\"#cb23-2\"></a></span>\n<span id=\"cb23-3\"><a href=\"#cb23-3\"></a><span class=\"dt\">void</span> findMean(<span class=\"dt\">int</span> i0, <span class=\"dt\">int</span> i1, <span class=\"dt\">int</span> j0, <span class=\"dt\">int</span> j1) {</span>\n<span id=\"cb23-4\"><a href=\"#cb23-4\"></a></span>\n<span id=\"cb23-5\"><a href=\"#cb23-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>findMean</code> is a function defined outside of <code>main</code>. Each run of <code>findMean</code> will remember the mean of the given subwindow that has the lowest variance seen so far.</p>\n<div class=\"sourceCode\" id=\"cb24\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb24-1\"><a href=\"#cb24-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb24-2\"><a href=\"#cb24-2\"></a></span>\n<span id=\"cb24-3\"><a href=\"#cb24-3\"></a>  meanTemp = vec4(<span class=\"dv\">0</span>);</span>\n<span id=\"cb24-4\"><a href=\"#cb24-4\"></a>  count    = <span class=\"dv\">0</span>;</span>\n<span id=\"cb24-5\"><a href=\"#cb24-5\"></a></span>\n<span id=\"cb24-6\"><a href=\"#cb24-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Make sure to reset <code>count</code> and <code>meanTemp</code> before computing the mean of the given subwindow.</p>\n<div class=\"sourceCode\" id=\"cb25\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb25-1\"><a href=\"#cb25-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb25-2\"><a href=\"#cb25-2\"></a></span>\n<span id=\"cb25-3\"><a href=\"#cb25-3\"></a>  <span class=\"cf\">for</span> (i = i0; i &lt;= i1; ++i) {</span>\n<span id=\"cb25-4\"><a href=\"#cb25-4\"></a>    <span class=\"cf\">for</span> (j = j0; j &lt;= j1; ++j) {</span>\n<span id=\"cb25-5\"><a href=\"#cb25-5\"></a>      color  =</span>\n<span id=\"cb25-6\"><a href=\"#cb25-6\"></a>        texture</span>\n<span id=\"cb25-7\"><a href=\"#cb25-7\"></a>          ( colorTexture</span>\n<span id=\"cb25-8\"><a href=\"#cb25-8\"></a>          ,   (gl_FragCoord.xy + vec2(i, j))</span>\n<span id=\"cb25-9\"><a href=\"#cb25-9\"></a>            / texSize</span>\n<span id=\"cb25-10\"><a href=\"#cb25-10\"></a>          );</span>\n<span id=\"cb25-11\"><a href=\"#cb25-11\"></a></span>\n<span id=\"cb25-12\"><a href=\"#cb25-12\"></a>      meanTemp += color;</span>\n<span id=\"cb25-13\"><a href=\"#cb25-13\"></a></span>\n<span id=\"cb25-14\"><a href=\"#cb25-14\"></a>      values[count] = dot(color.rgb, valueRatios);</span>\n<span id=\"cb25-15\"><a href=\"#cb25-15\"></a></span>\n<span id=\"cb25-16\"><a href=\"#cb25-16\"></a>      count += <span class=\"dv\">1</span>;</span>\n<span id=\"cb25-17\"><a href=\"#cb25-17\"></a>    }</span>\n<span id=\"cb25-18\"><a href=\"#cb25-18\"></a>  }</span>\n<span id=\"cb25-19\"><a href=\"#cb25-19\"></a></span>\n<span id=\"cb25-20\"><a href=\"#cb25-20\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Similar to the box blur, loop through the given subwindow and add up each color. At the same time, make sure to store the greyscale value for this sample in <code>values</code>.</p>\n<div class=\"sourceCode\" id=\"cb26\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb26-1\"><a href=\"#cb26-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb26-2\"><a href=\"#cb26-2\"></a></span>\n<span id=\"cb26-3\"><a href=\"#cb26-3\"></a>  meanTemp.rgb /= count;</span>\n<span id=\"cb26-4\"><a href=\"#cb26-4\"></a>  valueMean     = dot(meanTemp.rgb, valueRatios);</span>\n<span id=\"cb26-5\"><a href=\"#cb26-5\"></a></span>\n<span id=\"cb26-6\"><a href=\"#cb26-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To compute the mean, divide the samples sum by the number of samples taken. Calculate the greyscale value for the mean.</p>\n<div class=\"sourceCode\" id=\"cb27\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb27-1\"><a href=\"#cb27-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb27-2\"><a href=\"#cb27-2\"></a></span>\n<span id=\"cb27-3\"><a href=\"#cb27-3\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; count; ++i) {</span>\n<span id=\"cb27-4\"><a href=\"#cb27-4\"></a>    variance += pow(values[i] - valueMean, <span class=\"dv\">2</span>);</span>\n<span id=\"cb27-5\"><a href=\"#cb27-5\"></a>  }</span>\n<span id=\"cb27-6\"><a href=\"#cb27-6\"></a></span>\n<span id=\"cb27-7\"><a href=\"#cb27-7\"></a>  variance /= count;</span>\n<span id=\"cb27-8\"><a href=\"#cb27-8\"></a></span>\n<span id=\"cb27-9\"><a href=\"#cb27-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now calculate the variance for this given subwindow. The variance is the average squared difference between each sample's greyscale value the mean greyscale value.</p>\n<div class=\"sourceCode\" id=\"cb28\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb28-1\"><a href=\"#cb28-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb28-2\"><a href=\"#cb28-2\"></a></span>\n<span id=\"cb28-3\"><a href=\"#cb28-3\"></a>  <span class=\"cf\">if</span> (variance &lt; minVariance || minVariance &lt;= -<span class=\"dv\">1</span>) {</span>\n<span id=\"cb28-4\"><a href=\"#cb28-4\"></a>    mean = meanTemp;</span>\n<span id=\"cb28-5\"><a href=\"#cb28-5\"></a>    minVariance = variance;</span>\n<span id=\"cb28-6\"><a href=\"#cb28-6\"></a>  }</span>\n<span id=\"cb28-7\"><a href=\"#cb28-7\"></a>}</span>\n<span id=\"cb28-8\"><a href=\"#cb28-8\"></a></span>\n<span id=\"cb28-9\"><a href=\"#cb28-9\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the variance is smaller than what you've seen before or this is the first variance you've seen, set the mean of this subwindow as the final mean and update the minimum variance seen so far.</p>\n<div class=\"sourceCode\" id=\"cb29\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb29-1\"><a href=\"#cb29-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb29-2\"><a href=\"#cb29-2\"></a></span>\n<span id=\"cb29-3\"><a href=\"#cb29-3\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb29-4\"><a href=\"#cb29-4\"></a>  <span class=\"dt\">int</span> size = <span class=\"dt\">int</span>(parameters.x);</span>\n<span id=\"cb29-5\"><a href=\"#cb29-5\"></a>  <span class=\"cf\">if</span> (size &lt;= <span class=\"dv\">0</span>) { fragColor = texture(colorTexture, texCoord); <span class=\"cf\">return</span>; }</span>\n<span id=\"cb29-6\"><a href=\"#cb29-6\"></a></span>\n<span id=\"cb29-7\"><a href=\"#cb29-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Back in <code>main</code>, set the <code>size</code> parameter. If the size is at or below zero, return the fragment unchanged.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuLbLKO.gif\" alt=\"Kuwahara Kernal\" title=\"Kuwahara Kernal\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb30\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb30-1\"><a href=\"#cb30-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb30-2\"><a href=\"#cb30-2\"></a></span>\n<span id=\"cb30-3\"><a href=\"#cb30-3\"></a>  <span class=\"co\">// Lower Left</span></span>\n<span id=\"cb30-4\"><a href=\"#cb30-4\"></a></span>\n<span id=\"cb30-5\"><a href=\"#cb30-5\"></a>  findMean(-size, <span class=\"dv\">0</span>, -size, <span class=\"dv\">0</span>);</span>\n<span id=\"cb30-6\"><a href=\"#cb30-6\"></a></span>\n<span id=\"cb30-7\"><a href=\"#cb30-7\"></a>  <span class=\"co\">// Upper Right</span></span>\n<span id=\"cb30-8\"><a href=\"#cb30-8\"></a></span>\n<span id=\"cb30-9\"><a href=\"#cb30-9\"></a>  findMean(<span class=\"dv\">0</span>, size, <span class=\"dv\">0</span>, size);</span>\n<span id=\"cb30-10\"><a href=\"#cb30-10\"></a></span>\n<span id=\"cb30-11\"><a href=\"#cb30-11\"></a>  <span class=\"co\">// Upper Left</span></span>\n<span id=\"cb30-12\"><a href=\"#cb30-12\"></a></span>\n<span id=\"cb30-13\"><a href=\"#cb30-13\"></a>  findMean(-size, <span class=\"dv\">0</span>, <span class=\"dv\">0</span>, size);</span>\n<span id=\"cb30-14\"><a href=\"#cb30-14\"></a></span>\n<span id=\"cb30-15\"><a href=\"#cb30-15\"></a>  <span class=\"co\">// Lower Right</span></span>\n<span id=\"cb30-16\"><a href=\"#cb30-16\"></a></span>\n<span id=\"cb30-17\"><a href=\"#cb30-17\"></a>  findMean(<span class=\"dv\">0</span>, size, -size, <span class=\"dv\">0</span>);</span>\n<span id=\"cb30-18\"><a href=\"#cb30-18\"></a></span>\n<span id=\"cb30-19\"><a href=\"#cb30-19\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>As stated above, the Kuwahara filter works by computing the variance of four subwindows and then using the mean of the subwindow with the lowest variance as the final fragment color. Note that the four subwindows overlap each other.</p>\n<div class=\"sourceCode\" id=\"cb31\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb31-1\"><a href=\"#cb31-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb31-2\"><a href=\"#cb31-2\"></a></span>\n<span id=\"cb31-3\"><a href=\"#cb31-3\"></a>  mean.a    = <span class=\"dv\">1</span>;</span>\n<span id=\"cb31-4\"><a href=\"#cb31-4\"></a>  fragColor = mean;</span>\n<span id=\"cb31-5\"><a href=\"#cb31-5\"></a></span>\n<span id=\"cb31-6\"><a href=\"#cb31-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>After computing the variance and mean for each subwindow, set the fragment color to the mean of the subwindow with the lowest variance.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag\" target=\"_blank\" rel=\"noopener noreferrer\">box-blur.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/median-filter.frag\" target=\"_blank\" rel=\"noopener noreferrer\">median-filter.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/kuwahara-filter.frag\" target=\"_blank\" rel=\"noopener noreferrer\">kuwahara-filter.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"fog.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"bloom.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/building-the-demo.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Building The Demo | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Building The Demo | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Building The Demo | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"setup.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"running-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"building-the-demo\">Building The Demo</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/PQcDnIu.gif\" alt=\"Building The Demo\" title=\"Building The Demo\">\n</p>\n\n<p>Before you can try out the demo program, you'll have to build the example code first.</p>\n<h3 id=\"dependencies\">Dependencies</h3>\n<p>Before you can compile the example code, you'll need to install <a href=\"https://www.panda3d.org/\">Panda3D</a> for your platform. Panda3D is available for Linux, Mac, and Windows.</p>\n<h3 id=\"linux\">Linux</h3>\n<p>Start by <a href=\"https://www.panda3d.org/manual/?title=Installing_Panda3D_in_Linux\">installing</a> the <a href=\"https://www.panda3d.org/download/sdk-1-10-9/\">Panda3D SDK</a> for your distribution.</p>\n<p>Make sure to locate where the Panda3D headers and libraries are. The headers and libraries are most likely in <code>/usr/include/panda3d/</code> and <code>/usr/lib/panda3d/</code> respectively.</p>\n<p>Next clone this repository and change directory into it.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"fu\">git</span> clone https://github.com/lettier/3d-game-shaders-for-beginners.git</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a><span class=\"bu\">cd</span> 3d-game-shaders-for-beginners/demonstration</span></code></pre></div>\n<p>Now compile the source code into an object file.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"ex\">g++</span> \\</span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a>  -c src/main.cxx \\</span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  -o 3d-game-shaders-for-beginners.o \\</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  -std=gnu++11 \\</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  -O2 \\</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  -I/path/to/python/include/ \\</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  -I/path/to/panda3d/include/</span></code></pre></div>\n<p>With the object file created, create the executable by linking the object file to its dependencies.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"ex\">g++</span> \\</span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a>  3d-game-shaders-for-beginners.o \\</span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  -o 3d-game-shaders-for-beginners \\</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  -L/path/to/panda3d/lib \\</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  -lp3framework \\</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  -lpanda \\</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  -lpandafx \\</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  -lpandaexpress \\</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  -lpandaphysics \\</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  -lp3dtoolconfig \\</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a>  -lp3dtool \\</span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>  -lpthread</span></code></pre></div>\n<p>For more help, see the <a href=\"https://www.panda3d.org/manual/?title=How_to_compile_a_C++_Panda3D_program_on_Linux\">Panda3D manual</a>.</p>\n<h3 id=\"mac\">Mac</h3>\n<p>Start by installing the <a href=\"https://www.panda3d.org/download/sdk-1-10-9/\">Panda3D SDK</a> for Mac.</p>\n<p>Make sure to locate where the Panda3D headers and libraries are.</p>\n<p>Next clone this repository and change directory into it.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a><span class=\"fu\">git</span> clone https://github.com/lettier/3d-game-shaders-for-beginners.git</span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a><span class=\"bu\">cd</span> 3d-game-shaders-for-beginners</span></code></pre></div>\n<p>Now compile the source code into an object file. You'll have to find where the Python 2.7 and Panda3D include directories are.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a><span class=\"fu\">clang</span>++ \\</span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a>  -c main.cxx \\</span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  -o 3d-game-shaders-for-beginners.o \\</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  -std=gnu++11 \\</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  -g \\</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>  -O2 \\</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>  -I/path/to/python/include/ \\</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>  -I/path/to/panda3d/include/</span></code></pre></div>\n<p>With the object file created, create the executable by linking the object file to its dependencies. You'll need to track down where the Panda3D libraries are located.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a><span class=\"fu\">clang</span>++ \\</span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a>  3d-game-shaders-for-beginners.o \\</span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  -o 3d-game-shaders-for-beginners \\</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>  -L/path/to/panda3d/lib \\</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>  -lp3framework \\</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  -lpanda \\</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>  -lpandafx \\</span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>  -lpandaexpress \\</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>  -lpandaphysics \\</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a>  -lp3dtoolconfig \\</span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a>  -lp3dtool \\</span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\"></a>  -lpthread</span></code></pre></div>\n<p>For more help, see the <a href=\"https://www.panda3d.org/manual/?title=How_to_compile_a_C++_Panda3D_program_on_macOS\">Panda3D manual</a>.</p>\n<h3 id=\"windows\">Windows</h3>\n<p>Start by <a href=\"https://www.panda3d.org/manual/?title=Installing_Panda3D_in_Windows\">installing</a> the <a href=\"https://www.panda3d.org/download/sdk-1-10-9/\">Panda3D SDK</a> for Windows.</p>\n<p>Make sure to locate where the Panda3D headers and libraries are.</p>\n<p>Next clone this repository and change directory into it.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a><span class=\"fu\">git</span> clone https://github.com/lettier/3d-game-shaders-for-beginners.git</span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a><span class=\"bu\">cd</span> 3d-game-shaders-for-beginners</span></code></pre></div>\n<p>For more help, see the <a href=\"https://www.panda3d.org/manual/?title=Running_your_Program&amp;language=cxx\">Panda3D manual</a>.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"setup.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"running-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/cel-shading.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Cel Shading | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Cel Shading | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Cel Shading | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"rim-lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"normal-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"cel-shading\">Cel Shading</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/W80Ke1y.gif\" alt=\"Cel Shaded\" title=\"Cel Shaded\">\n</p>\n\n<p>Cel shading is a technique to make 3D objects look 2D or flat. In 2D, you can make an object look 3D by applying a smooth gradient. However, with cel shading, you're breaking up the gradients into abrupt transitions. Typically there is only one transition where the shading goes from fully lit to fully shadowed. When combined with <a href=\"outlining.html\">outlining</a>, cel shading can really sell the 2D cartoon look.</p>\n<h2 id=\"diffuse\">Diffuse</h2>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>    <span class=\"dt\">float</span> diffuseIntensity = max(dot(normal, unitLightDirection), <span class=\"fl\">0.0</span>);</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>          diffuseIntensity = step(<span class=\"fl\">0.1</span>, diffuseIntensity);</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a></span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Revisiting the <a href=\"lighting.html#diffuse\">lighting</a> model, modify the <code>diffuseIntensity</code> such that it is either zero or one.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/lyLweFc.png\" alt=\"Step Function\" title=\"Step Function\">\n</p>\n\n<p>The <code>step</code> function returns zero if the input is less than the edge and one otherwise.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EI6QJ60.png\" alt=\"Steps Function\" title=\"Steps Function\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"cf\">if</span>      (diffuseIntensity &gt;= <span class=\"fl\">0.8</span>) { diffuseIntensity = <span class=\"fl\">1.0</span>; }</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  <span class=\"cf\">else</span> <span class=\"cf\">if</span> (diffuseIntensity &gt;= <span class=\"fl\">0.6</span>) { diffuseIntensity = <span class=\"fl\">0.6</span>; }</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"cf\">else</span> <span class=\"cf\">if</span> (diffuseIntensity &gt;= <span class=\"fl\">0.3</span>) { diffuseIntensity = <span class=\"fl\">0.3</span>; }</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"cf\">else</span>                              { diffuseIntensity = <span class=\"fl\">0.0</span>; }</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a></span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If you would like to have a few steps or transitions, you can perform something like the above.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7KK65mi.png\" alt=\"Step Texture\" title=\"Step Texture\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  diffuseIntensity = texture(steps, vec2(diffuseIntensity, <span class=\"fl\">0.0</span>)).r;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Another approach is to put your step values into a texture with the transitions going from darker to lighter. Using the <code>diffuseIntensity</code> as a U coordinate, it will automatically transform itself.</p>\n<h2 id=\"specular\">Specular</h2>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a></span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>      <span class=\"dt\">float</span> specularIntensity = clamp(dot(normal, halfwayDirection), <span class=\"fl\">0.0</span>, <span class=\"fl\">1.0</span>);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>            specularIntensity = step(<span class=\"fl\">0.98</span>, specularIntensity);</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the <code>step</code> function again, set the <code>specularIntensity</code> to be either zero or one. You can also use one of the other approaches described up above for the specular highlight as well. After you've altered the <code>specularIntensity</code>, the rest of the lighting calculations are the same.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"rim-lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"normal-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/chromatic-aberration.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Chromatic Aberration | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Chromatic Aberration | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Chromatic Aberration | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"motion-blur.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"screen-space-reflection.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"chromatic-aberration\">Chromatic Aberration</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bawgERm.gif\" alt=\"Chromatic Aberration\" title=\"Chromatic Aberration\">\n</p>\n\n<p>Chromatic aberration is a screen space technique that simulates lens distortion. Use it to give your scene a cinematic, lo-fi analog feel or to emphasize a chaotic event.</p>\n<h3 id=\"texture\">Texture</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>uniform sampler2D colorTexture;</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The input texture needed is the scene's colors captured into a framebuffer texture.</p>\n<h3 id=\"parameters\">Parameters</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fNpMaPL.gif\" alt=\"Chromatic Aberration\" title=\"Chromatic Aberration\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> redOffset   =  <span class=\"fl\">0.009</span>;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  <span class=\"dt\">float</span> greenOffset =  <span class=\"fl\">0.006</span>;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"dt\">float</span> blueOffset  = -<span class=\"fl\">0.006</span>;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The adjustable parameters for this technique are the red, green, and blue offsets. Feel free to play around with these to get the particular color fringe you're looking for. These particular offsets produce a yellowish orange and blue fringe.</p>\n<h3 id=\"direction\">Direction</h3>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform vec2 mouseFocusPoint;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a></span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  vec2 direction = texCoord - mouseFocusPoint;</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The offsets can occur horizontally, vertically, or radially. One approach is to radiate out from the <a href=\"depth-of-field.html\">depth of field</a> focal point. As the scene gets more out of focus, the chromatic aberration increases.</p>\n<h3 id=\"samples\">Samples</h3>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>out vec4 fragColor;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>  fragColor.r  = texture(colorTexture, texCoord + (direction * vec2(redOffset  ))).r;</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>  fragColor.g  = texture(colorTexture, texCoord + (direction * vec2(greenOffset))).g;</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>  fragColor.ba = texture(colorTexture, texCoord + (direction * vec2(blueOffset ))).ba;</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>}</span></code></pre></div>\n<p>With the direction and offsets, make three samples of the scene's colors—one for the red, green, and blue channels. These will be the final fragment color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/chromatic-aberration.frag\" target=\"_blank\" rel=\"noopener noreferrer\">chromatic-aberration.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2021 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"motion-blur.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"screen-space-reflection.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/deferred-rendering.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Deferred Rendering | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Deferred Rendering | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Deferred Rendering | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"normal-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"fog.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"deferred-rendering\">Deferred Rendering</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/rxTYLGv.png\" alt=\"Deferred vs Forward\" title=\"Deferred vs Forward\">\n</p>\n\n<p>Deferred rendering (deferred shading) is a screen space lighting technique. Instead of calculating the lighting for a scene while you traverse its geometry—you defer or wait to perform the lighting calculations until after the scene's geometry fragments have been culled or discarded. This can give you a performance boost depending on the complexity of your scene.</p>\n<h3 id=\"phases\">Phases</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uTimQJj.gif\" alt=\"G-buffer\" title=\"G-buffer\">\n</p>\n\n<p>Deferred rendering is performed in two phases. The first phase involves going through the scene's geometry and rendering its positions or depths, normals, and materials into a framebuffer known as the geometry buffer or G-buffer. With the exception of some transformations, this is mostly a read-only phase so its performance cost is minimal. After this phase, you're only dealing with 2D textures in the shape of the screen.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/frYp6to.gif\" alt=\"Lighting Phase\" title=\"Lighting Phase\">\n</p>\n\n<p>The second and last phase is where you perform your lighting calculations using the output of the first phase. This is when you calculate the ambient, diffuse, and specular colors. Shadow and normal mapping are performed in this phase as well.</p>\n<h3 id=\"advantages\">Advantages</h3>\n<p>The reason for using deferred rendering is to reduce the number of lighting calculations made. With forward rendering, the number of lighting calculations scales with the number of fragments and lights. However, with deferred shading, the number of lighting calculations scales with the number of pixels and lights. Recall that for a single pixel, there can be multiple fragments produced. As you add geometry, the number of lighting calculations per pixel increases when using forward but not when using deferred.</p>\n<p>For simple scenes, deferred rendering doesn't provide much of a performance boost and may even hurt performance. However, for complex scenes with lots of lighting, it becomes the better option. Deferred becomes faster than forward because you're only calculating the lighting per light, per pixel. In forward rendering, you're calculating the lighting per light per fragment which can be multiple times per pixel.</p>\n<h3 id=\"disadvantages\">Disadvantages</h3>\n<p>Deferred rendering allows you render complex scenes using many lights but it does come with its own set of tradeoffs. Transparency becomes an issue because the geometry data you could see through a semitransparent object is discarded in the first phase. Other tradesoffs include increased memory consumption due to the G-buffer and the extra workarounds needed to deal with aliasing.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"normal-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"fog.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/depth-of-field.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Depth Of Field | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Depth Of Field | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Depth Of Field | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"outlining.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"posterization.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"depth-of-field\">Depth Of Field</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/DEa77Bh.gif\" alt=\"Depth Of Field\" title=\"Depth Of Field\">\n</p>\n\n<p>Like <a href=\"ssao.html\">SSAO</a>, depth of field is an effect you can't live without once you've used it. Artistically, you can use it to draw your viewer's eye to a certain subject. But in general, depth of field adds a lot of realism for a little bit of effort.</p>\n<h3 id=\"in-focus\">In Focus</h3>\n<p>The first step is to render your scene completely in focus. Be sure to render this into a framebuffer texture. This will be one of the inputs to the depth of field shader.</p>\n<h3 id=\"out-of-focus\">Out Of Focus</h3>\n<p>The second step is to blur the scene as if it was completely out of focus. Like bloom and SSAO, you can use a <a href=\"blur.html#box-blur\">box blur</a>. Be sure to render this out-of-focus-scene into a framebuffer texture. This will be one of the inputs to the depth of field shader.</p>\n<h4 id=\"bokeh\">Bokeh</h4>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/aQ9Ga8J.gif\" alt=\"Bokeh\" title=\"Bokeh\">\n</p>\n\n<p>For a great bokeh effect, dilate the out of focus texture and use that as the out of focus input. See <a href=\"dilation.html\">dilation</a> for more details.</p>\n<h3 id=\"mixing\">Mixing</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">float</span> minDistance = <span class=\"fl\">1.0</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  <span class=\"dt\">float</span> maxDistance = <span class=\"fl\">3.0</span>;</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a></span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Feel free to tweak these two parameters. All positions at or below <code>minDistance</code> will be completely in focus. All positions at or beyond <code>maxDistance</code> will be completely out of focus.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  vec4 focusColor      = texture(focusTexture, texCoord);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  vec4 outOfFocusColor = texture(outOfFocusTexture, texCoord);</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You'll need the in focus and out of focus colors.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  vec4 position = texture(positionTexture, texCoord);</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You'll also need the vertex position in view space. You can reuse the position framebuffer texture you used for <a href=\"ssao.html#vertex-positions\">SSAO</a>.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec4 focusPoint = texture(positionTexture, mouseFocusPoint);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The focus point is a position somewhere in your scene. All of the points in your scene are measured from the focus point.</p>\n<p>Choosing the focus point is up to you. The demo uses the scene position directly under the mouse when clicking the middle mouse button. However, it could be a constant distance from the camera or a static position.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/idDZr62.png\" alt=\"smoothstep\" title=\"smoothstep\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  <span class=\"dt\">float</span> blur =</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>    smoothstep</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>      ( minDistance</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>      , maxDistance</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>      , abs(position.y - focusPoint.y)</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>      );</span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a></span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>smoothstep</code> returns values from zero to one. The <code>minDistance</code> is the left-most edge. Any position less than the minimum distance, from the focus point, will be in focus or have a blur of zero. The <code>maxDistance</code> is the right-most edge. Any position greater than the maximum distance, from the focus point, will be out of focus or have a blur or one. For distances between the edges, blur will be between zero and one. These values are interpolated along a s-shaped curve.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a></span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>  fragColor = mix(focusColor, outOfFocusColor, blur);</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>fragColor</code> is a mixture of the in focus and out of focus color. The closer <code>blur</code> is to one, the more it will use the <code>outOfFocusColor</code>. Zero <code>blur</code> means this fragment is entirely in focus. At <code>blur &gt;= 1</code>, this fragment is completely out of focus.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag\" target=\"_blank\" rel=\"noopener noreferrer\">box-blur.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/depth-of-field.frag\" target=\"_blank\" rel=\"noopener noreferrer\">depth-of-field.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"outlining.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"posterization.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/dilation.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Dilation | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Dilation | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Dilation | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"sharpen.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"film-grain.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"dilation\">Dilation</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/z751O74.gif\" alt=\"Dilation\" title=\"Dilation\">\n</p>\n\n<p>Dilation dilates or enlarges the brighter areas of an image while at the same time, contracts or shrinks the darker areas of an image. This tends to create a pillowy look. You can use dilation for a glow/bloom effect or to add bokeh to your <a href=\"depth-of-field.html\">depth of field</a>.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">int</span>   size         = <span class=\"dt\">int</span>(parameters.x);</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  <span class=\"dt\">float</span> separation   =     parameters.y;</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"dt\">float</span> minThreshold = <span class=\"fl\">0.1</span>;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>  <span class=\"dt\">float</span> maxThreshold = <span class=\"fl\">0.3</span>;</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a></span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>size</code> and <code>separation</code> parameters control how dilated the image becomes. A larger <code>size</code> will increase the dilation at the cost of performance. A larger <code>separation</code> will increase the dilation at the cost of quality. The <code>minThreshold</code> and <code>maxThreshold</code> parameters control which parts of the image become dilated.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  vec2 texSize   = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  vec2 fragCoord = gl_FragCoord.xy;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  fragColor = texture(colorTexture, fragCoord / texSize);</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a></span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the color at the current fragment's position.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  <span class=\"dt\">float</span>  mx = <span class=\"fl\">0.0</span>;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  vec4  cmx = fragColor;</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = -size; i &lt;= size; ++i) {</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>    <span class=\"cf\">for</span> (<span class=\"dt\">int</span> j = -size; j &lt;= size; ++j) {</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>    }</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  }</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Loop through a <code>size</code> by <code>size</code> window, centered at the current fragment position. As you loop, find the brightest color based on the surrounding greyscale values.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/X3uIyIL.png\" alt=\"Dilation Window\" title=\"Dilation Window\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>      <span class=\"co\">// For a rectangular shape.</span></span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>      <span class=\"co\">//if (false);</span></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>      <span class=\"co\">// For a diamond shape;</span></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>      <span class=\"co\">//if (!(abs(i) &lt;= size - abs(j))) { continue; }</span></span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a></span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>      <span class=\"co\">// For a circular shape.</span></span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>      <span class=\"cf\">if</span> (!(distance(vec2(i, j), vec2(<span class=\"dv\">0</span>, <span class=\"dv\">0</span>)) &lt;= size)) { <span class=\"cf\">continue</span>; }</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a></span>\n<span id=\"cb4-12\"><a href=\"#cb4-12\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The window shape will determine the shape of the dilated parts of the image. For a rectangular shape, you can use every fragment covered by the window. For any other shape, skip the fragments that fall outside the desired shape.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>      vec4 c =</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>        texture</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>          ( colorTexture</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>          ,   ( gl_FragCoord.xy</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>              + (vec2(i, j) * separation)</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>              )</span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>            / texSize</span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a>          );</span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\"></a></span>\n<span id=\"cb5-12\"><a href=\"#cb5-12\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample a fragment color from the surrounding window.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>      <span class=\"dt\">float</span> mxt = dot(c.rgb, vec3(<span class=\"fl\">0.21</span>, <span class=\"fl\">0.72</span>, <span class=\"fl\">0.07</span>));</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a></span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Convert the sampled color to a greyscale value.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>      <span class=\"cf\">if</span> (mxt &gt; mx) {</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>        mx  = mxt;</span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>        cmx = c;</span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>      }</span>\n<span id=\"cb7-7\"><a href=\"#cb7-7\"></a></span>\n<span id=\"cb7-8\"><a href=\"#cb7-8\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the sampled greyscale value is larger than the current maximum greyscale value, update the maximum greyscale value and its corresponding color.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  fragColor.rgb =</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>    mix</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>      ( fragColor.rgb</span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>      , cmx.rgb</span>\n<span id=\"cb8-7\"><a href=\"#cb8-7\"></a>      , smoothstep(minThreshold, maxThreshold, mx)</span>\n<span id=\"cb8-8\"><a href=\"#cb8-8\"></a>      );</span>\n<span id=\"cb8-9\"><a href=\"#cb8-9\"></a></span>\n<span id=\"cb8-10\"><a href=\"#cb8-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The new fragment color is a mixture between the existing fragment color and the brightest color found. If the maximum greyscale value found is less than <code>minThreshold</code>, the fragment color is unchanged. If the maximum greyscale value is greater than <code>maxThreshold</code>, the fragment color is replaced with the brightest color found. For any other case, the fragment color is a mix between the current fragment color and the brightest color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/dilation.frag\" target=\"_blank\" rel=\"noopener noreferrer\">dilation.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"sharpen.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"film-grain.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/film-grain.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Film Grain | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Film Grain | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Film Grain | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"dilation.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"lookup-table.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"film-grain\">Film Grain</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ct7mTv5.gif\" alt=\"Film Grain\" title=\"Film Grain\">\n</p>\n\n<p>Film grain (when applied in subtle doses, unlike here) can add a bit of realism you don't notice until it's removed. Typically, it's the imperfections that make a digitally generated image more believable. In terms of the shader graph, film grain is usually the last effect applied before the game is put on screen.</p>\n<h3 id=\"amount\">Amount</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">float</span> amount = <span class=\"fl\">0.1</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>amount</code> controls how noticeable the film grain is. Crank it up for a snowy picture.</p>\n<h3 id=\"random-intensity\">Random Intensity</h3>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform <span class=\"dt\">float</span> osg_FrameTime;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">//...</span></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  <span class=\"dt\">float</span> toRadians = <span class=\"fl\">3.14</span> / <span class=\"dv\">180</span>;</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a></span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>    <span class=\"co\">//...</span></span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a></span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>    <span class=\"dt\">float</span> randomIntensity =</span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a>      fract</span>\n<span id=\"cb2-13\"><a href=\"#cb2-13\"></a>        ( <span class=\"dv\">10000</span></span>\n<span id=\"cb2-14\"><a href=\"#cb2-14\"></a>        * sin</span>\n<span id=\"cb2-15\"><a href=\"#cb2-15\"></a>            (</span>\n<span id=\"cb2-16\"><a href=\"#cb2-16\"></a>              ( gl_FragCoord.x</span>\n<span id=\"cb2-17\"><a href=\"#cb2-17\"></a>              + gl_FragCoord.y</span>\n<span id=\"cb2-18\"><a href=\"#cb2-18\"></a>              * osg_FrameTime</span>\n<span id=\"cb2-19\"><a href=\"#cb2-19\"></a>              )</span>\n<span id=\"cb2-20\"><a href=\"#cb2-20\"></a>            * toRadians</span>\n<span id=\"cb2-21\"><a href=\"#cb2-21\"></a>            )</span>\n<span id=\"cb2-22\"><a href=\"#cb2-22\"></a>        );</span>\n<span id=\"cb2-23\"><a href=\"#cb2-23\"></a></span>\n<span id=\"cb2-24\"><a href=\"#cb2-24\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>This snippet calculates the random intensity needed to adjust the amount.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>Time Since F1 = <span class=\"bn\">00</span> <span class=\"bn\">01</span> <span class=\"bn\">02</span> <span class=\"bn\">03</span> <span class=\"bn\">04</span> <span class=\"bn\">05</span> <span class=\"bn\">06</span> <span class=\"bn\">07</span> <span class=\"er\">08</span> <span class=\"er\">09</span> <span class=\"dv\">10</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a>Frame Number  = F1    F3    F4       F5 F6</span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>osg_FrameTime = <span class=\"bn\">00</span>    <span class=\"bn\">02</span>    <span class=\"bn\">04</span>       <span class=\"bn\">07</span> <span class=\"er\">08</span></span></code></pre></div>\n<p><code>osg_FrameTime</code> is <a href=\"https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L930\">provided</a> by Panda3D. The frame time is a timestamp of how many seconds have passed since the first frame. The example code uses this to animate the film grain as <code>osg_FrameTime</code> will always be different each frame.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a>              <span class=\"co\">// ...</span></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a></span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>              ( gl_FragCoord.x</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>              + gl_FragCoord.y</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>              * <span class=\"dv\">8009</span> <span class=\"co\">// Large number here.</span></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a></span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>              <span class=\"co\">// ...</span></span></code></pre></div>\n<p>For static film grain, replace <code>osg_FrameTime</code> with a large number. You may have to try different numbers to avoid seeing any patterns.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/xqSIMCb.gif\" alt=\"Horizontal, vertical, and diagonal lines.\" title=\"Horizontal, vertical, and diagonal lines.\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>        <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>        * sin</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>            (</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>              ( gl_FragCoord.x</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>              + gl_FragCoord.y</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>              * someNumber</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a></span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>              <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Both the x and y coordinate are used to create points or specs of film grain. If only x was used, there would only be vertical lines. Similarly, if only y was used, there would be only horizontal lines.</p>\n<p>The reason the snippet multiplies one coordinate by some number is to break up the diagonal symmetry.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4UXllmS.gif\" alt=\"Rain\" title=\"Rain\">\n</p>\n\n<p>You can of course remove the coordinate multiplier for a somewhat decent looking rain effect. To animate the rain effect, multiply the output of <code>sin</code> by <code>osg_FrameTime</code>.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>              <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>              ( gl_FragCoord.x</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>              + gl_FragCoord.y</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>              <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Play around with the x and y coordinate to try and get the rain to change directions. Keep only the x coordinate for a straight downpour.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>input = (gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime) * toRadians</span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a>  frame(<span class=\"dv\">10000</span> * sin(input)) =</span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>    fract(<span class=\"dv\">10000</span> * sin(<span class=\"fl\">6.977777777777778</span>)) =</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>      fract(<span class=\"dv\">10000</span> * <span class=\"fl\">0.6400723818964882</span>) =</span></code></pre></div>\n<p><code>sin</code> is used as a hashing function. The fragment's coordinates are hashed to some output of <code>sin</code>. This has the nice property that no matter the input (big or small), the output range is negative one to one.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>fract(<span class=\"dv\">10000</span> * sin(<span class=\"fl\">6.977777777777778</span>)) =</span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a>  fract(<span class=\"dv\">10000</span> * <span class=\"fl\">0.6400723818964882</span>) =</span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>    fract(<span class=\"fl\">6400.723818964882</span>) =</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>      <span class=\"fl\">0.723818964882</span></span></code></pre></div>\n<p><code>sin</code> is also used as a pseudo random number generator when combined with <code>fract</code>.</p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode python\"><code class=\"sourceCode python\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a><span class=\"op\">&gt;&gt;&gt;</span> [floor(fract(<span class=\"dv\">4</span>     <span class=\"op\">*</span> sin(x <span class=\"op\">*</span> toRadians)) <span class=\"op\">*</span> <span class=\"dv\">10</span>) <span class=\"cf\">for</span> x <span class=\"kw\">in</span> <span class=\"bu\">range</span>(<span class=\"dv\">0</span>, <span class=\"dv\">10</span>)]</span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a>[<span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>, <span class=\"dv\">2</span>, <span class=\"dv\">2</span>, <span class=\"dv\">3</span>, <span class=\"dv\">4</span>, <span class=\"dv\">4</span>, <span class=\"dv\">5</span>, <span class=\"dv\">6</span>]</span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a></span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a><span class=\"op\">&gt;&gt;&gt;</span> [floor(fract(<span class=\"dv\">10000</span> <span class=\"op\">*</span> sin(x <span class=\"op\">*</span> toRadians)) <span class=\"op\">*</span> <span class=\"dv\">10</span>) <span class=\"cf\">for</span> x <span class=\"kw\">in</span> <span class=\"bu\">range</span>(<span class=\"dv\">0</span>, <span class=\"dv\">10</span>)]</span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>[<span class=\"dv\">0</span>, <span class=\"dv\">4</span>, <span class=\"dv\">8</span>, <span class=\"dv\">0</span>, <span class=\"dv\">2</span>, <span class=\"dv\">1</span>, <span class=\"dv\">7</span>, <span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">5</span>]</span></code></pre></div>\n<p>Take a look at the first sequence of numbers and then the second. Each sequence is deterministic but the second sequence has less of a pattern than the first. So while the output of <code>fract(10000 * sin(...))</code> is deterministic, it doesn't have much of a discernible pattern.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Mtt8BNg.gif\" alt=\"Increasing the pseudo randomness.\" title=\"Increasing the pseudo randomness.\">\n</p>\n\n<p>Here you see the <code>sin</code> multiplier going from 1, to 10, to 100, and then to 1000.</p>\n<p>As you increase the <code>sin</code> output multiplier, you get less and less of a pattern. This is the reason the snippet multiplies <code>sin</code> by 10,000.</p>\n<h3 id=\"fragment-color\">Fragment Color</h3>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a></span>\n<span id=\"cb10-6\"><a href=\"#cb10-6\"></a>  vec4 color = texture(colorTexture, texCoord);</span>\n<span id=\"cb10-7\"><a href=\"#cb10-7\"></a></span>\n<span id=\"cb10-8\"><a href=\"#cb10-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Convert the fragment's coordinates to UV coordinates. Using these UV coordinates, look up the texture color for this fragment.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>    amount *= randomIntensity;</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a></span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a>    color.rgb += amount;</span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a></span>\n<span id=\"cb11-7\"><a href=\"#cb11-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Adjust the amount by the random intensity and add this to the color.</p>\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>  fragColor = color;</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a></span>\n<span id=\"cb12-5\"><a href=\"#cb12-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Set the fragment color and you're done.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/film-grain.frag\" target=\"_blank\" rel=\"noopener noreferrer\">film-grain.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"dilation.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"lookup-table.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/flow-mapping.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Flow Mapping | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Flow Mapping | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Flow Mapping | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"foam.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"outlining.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"flow-mapping\">Flow Mapping</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3WDO9xW.gif\" alt=\"Flow Mapping\" title=\"Flow Mapping\">\n</p>\n\n<p>Flow mapping is useful when you need to animate some fluid material. Much like diffuse maps map UV coordinates to diffuse colors and normal maps map UV coordinates to normals, flow maps map UV coordinates to 2D translations or flows.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b9Vw94N.png\" alt=\"Flow Map\" title=\"Flow Map\">\n</p>\n\n<p>Here you see a flow map that maps UV coordinates to translations in the positive y-axis direction. Flow maps use the red and green channels to store translations in the x and y direction. The red channel is for the x-axis and the green channel is the y-axis. Both range from zero to one which translates to flows that range from <code>(-1, -1)</code> to <code>(1, 1)</code>. This flow map is all one color consisting of 0.5 red and 0.6 green.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>[r, g, b] =</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a>  [r * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>, g * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>, b * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>] =</span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>    [ x, y, z]</span></code></pre></div>\n<p>Recall how the colors in a normal map are converted to actual normals. There is a similar process for flow maps.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform sampler2D flowTexture;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  vec2 flow = texture(flowTexture, uv).xy;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>       flow = (flow - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span>;</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a></span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To convert a flow map color to a flow, you minus 0.5 from the channel (red and green) and multiply by two.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>(r, g) =</span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a> ( (r - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a> , (g - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span></span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a> ) =</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  ( (<span class=\"fl\">0.5</span> - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  , (<span class=\"fl\">0.6</span> - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  ) =</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>    (x, y) =</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>      (<span class=\"dv\">0</span>, <span class=\"fl\">0.2</span>)</span></code></pre></div>\n<p>The flow map above maps each UV coordinate to the flow <code>(0, 0.2)</code>. This indicates zero movement in the x direction and a movement of 0.2 in the y direction.</p>\n<p>The flows can be used to translate all sorts of things but they're typically used to offset the UV coordinates of a another texture.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/N6TWBw8.gif\" alt=\"Foam Mask\" title=\"Foam Mask\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec2 flow = texture(flowTexture, diffuseCoord).xy;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>       flow = (flow - <span class=\"fl\">0.5</span>) * <span class=\"dv\">2</span>;</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  vec4 foamPattern =</span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>    texture</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>      ( foamPatternTexture</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>      , vec2</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>          ( diffuseCoord.x + flow.x * osg_FrameTime</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a>          , diffuseCoord.y + flow.y * osg_FrameTime</span>\n<span id=\"cb4-12\"><a href=\"#cb4-12\"></a>          )</span>\n<span id=\"cb4-13\"><a href=\"#cb4-13\"></a>      );</span>\n<span id=\"cb4-14\"><a href=\"#cb4-14\"></a></span>\n<span id=\"cb4-15\"><a href=\"#cb4-15\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>For example, the demo program uses a flow map to animate the water. Here you see the flow map being used to animate the <a href=\"foam.html#mask\">foam mask</a>. This continuously moves the diffuse UV coordinates directly up, giving the foam mask the appearance of moving down stream.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>          <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>          ( diffuseCoord.x + flow.x * osg_FrameTime</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>          , diffuseCoord.y + flow.y * osg_FrameTime</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a></span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>          <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You'll need how many seconds have passed since the first frame in order to animate the UV coordinates in the direction indicated by the flow. <code>osg_FrameTime</code> is <a href=\"https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L930\">provided</a> by Panda3D. It is a timestamp of how many seconds have passed since the first frame.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"foam.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"outlining.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/foam.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Foam | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Foam | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Foam | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"screen-space-refraction.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"flow-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"foam\">Foam</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/SVLPYKn.gif\" alt=\"Foam\" title=\"Foam\">\n</p>\n\n<p>Foam is typically used when simulating some body of water. Anywhere the water's flow is disrupted, you add some foam. The foam isn't much by itself but it can really connect the water with the rest of the scene.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/HCqvd8c.gif\" alt=\"Lava River\" title=\"Lava River\">\n</p>\n\n<p>But don't stop at just water. You can use the same technique to make a river of lava for example.</p>\n<h3 id=\"vertex-positions\">Vertex Positions</h3>\n<p>Like <a href=\"screen-space-refraction.html\">screen space refraction</a>, you'll need both the foreground and background vertex positions. The foreground being the scene with the foamy surface and the background being the scene without the foamy surface. Referrer back to <a href=\"ssao.html#vertex-positions\">SSAO</a> for the details on how to acquire the vertex positions in view space.</p>\n<h3 id=\"mask\">Mask</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/N6TWBw8.gif\" alt=\"Foam Mask\" title=\"Foam Mask\">\n</p>\n\n<p>You'll need to texture your scene with a foam mask. The demo masks everything off except the water. For the water, it textures it with a foam pattern.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform sampler2D foamPatternTexture;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>in vec2 diffuseCoord;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>out vec4 fragColor;</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a></span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a>  vec4 foamPattern = texture(foamPatternTexture, diffuseCoord);</span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a></span>\n<span id=\"cb1-12\"><a href=\"#cb1-12\"></a>  fragColor = vec4(vec3(dot(foamPattern.rgb, vec3(<span class=\"dv\">1</span>)) / <span class=\"dv\">3</span>), <span class=\"dv\">1</span>);</span>\n<span id=\"cb1-13\"><a href=\"#cb1-13\"></a>}</span></code></pre></div>\n<p>Here you see the fragment shader that generates the foam mask. It takes a foam pattern texture and UV maps it to the scene's geometry using the diffuse UV coordinates. For every model, except the water, the shader is given a solid black texture as the <code>foamPatternTexture</code>.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  fragColor = vec4(vec3(dot(foamPattern.rgb, vec3(<span class=\"dv\">1</span>)) / <span class=\"dv\">3</span>), <span class=\"dv\">1</span>);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fragment color is converted to greyscale, as a precaution, since the foam shader expects the foam mask to be greyscale.</p>\n<h3 id=\"uniforms\">Uniforms</h3>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform sampler2D maskTexture;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>uniform sampler2D positionFromTexture;</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>uniform sampler2D positionToTexture;</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The foam shader accepts a mask texture, the foreground vertex positions (<code>positionFromTexture</code>), and the background vertex positions (<code>positionToTexture</code>).</p>\n<h3 id=\"parameters\">Parameters</h3>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  <span class=\"dt\">float</span> foamDepth = <span class=\"dv\">4</span>;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec4  foamColor = vec4(<span class=\"fl\">0.8</span>, <span class=\"fl\">0.85</span>, <span class=\"fl\">0.92</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The adjustable parameters for the foam shader are the foam depth and color. The foam depth controls how much foam is shown. As the foam depth increases, the amount of foam shown increases.</p>\n<h3 id=\"distance\">Distance</h3>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  vec4 positionFrom = texture(positionFromTexture, texCoord);</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  vec4 positionTo   = texture(positionToTexture,   texCoord);</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a></span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>  <span class=\"dt\">float</span> depth  = (positionTo.xyz - positionFrom.xyz).y;</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a></span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Compute the distance from the foreground position to the background position. Since the positions are in view (camera) space, we only need the y value since it goes into the screen.</p>\n<h3 id=\"amount\">Amount</h3>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  <span class=\"dt\">float</span> amount  = clamp(depth / foamDepth.x, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>        amount  = <span class=\"dv\">1</span> - amount;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>        amount *= mask.r;</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>        amount  = amount * amount / (<span class=\"dv\">2</span> * (amount * amount - amount) + <span class=\"dv\">1</span>);</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The amount of foam is based on the depth, the foam depth parameter, and the mask value.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/CDIPmin.png\" alt=\"Easing equation.\" title=\"Easing equation.\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>        <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>        amount  = amount * amount / (<span class=\"dv\">2</span> * (amount * amount - amount) + <span class=\"dv\">1</span>);</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>        <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Reshape the amount using the ease in and out easing function. This will give a lot of foam near depth zero and little to no foam near <code>foamDepth</code>.</p>\n<h3 id=\"fragment-color\">Fragment Color</h3>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  fragColor = mix(vec4(<span class=\"dv\">0</span>), foamColor, amount);</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a></span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fragment color is a mix between transparent black and the foam color based on the amount.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/foam-mask.frag\" target=\"_blank\" rel=\"noopener noreferrer\">foam-mask.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/foam.frag\" target=\"_blank\" rel=\"noopener noreferrer\">foam.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base-combine.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base-combine.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"screen-space-refraction.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"flow-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/fog.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Fog | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Fog | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Fog | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"deferred-rendering.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"blur.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"fog\">Fog</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uNRxZl4.gif\" alt=\"Fog\" title=\"Fog\">\n</p>\n\n<p>Fog (or mist as it's called in Blender) adds atmospheric haze to a scene, providing mystique and softening pop-ins (geometry suddenly entering into the camera's frustum).</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform vec4 color;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>uniform vec2 nearFar;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>To calculate the fog, you'll need its color, near distance, and far distance.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform sampler2D positionTexture;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  vec2 texSize  = textureSize(positionTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a></span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a>  vec4 position = texture(positionTexture, texCoord);</span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a></span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>In addition to the fog's attributes, you'll also need the fragment's vertex <code>position</code>.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"dt\">float</span> fogMin = <span class=\"fl\">0.00</span>;</span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a>  <span class=\"dt\">float</span> fogMax = <span class=\"fl\">0.97</span>;</span></code></pre></div>\n<p><code>fogMax</code> controls how much of the scene is still visible when the fog is most intense. <code>fogMin</code> controls how much of the fog is still visible when the fog is least intense.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  <span class=\"dt\">float</span> near = nearFar.x;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  <span class=\"dt\">float</span> far  = nearFar.y;</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"dt\">float</span> intensity =</span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>    clamp</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>      (   (position.y - near)</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>        / (far        - near)</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>      , fogMin</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a>      , fogMax</span>\n<span id=\"cb4-12\"><a href=\"#cb4-12\"></a>      );</span>\n<span id=\"cb4-13\"><a href=\"#cb4-13\"></a></span>\n<span id=\"cb4-14\"><a href=\"#cb4-14\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The example code uses a linear model for calculating the fog's intensity. There's also an exponential model you could use.</p>\n<p>The fog's intensity is <code>fogMin</code> before or at the start of the fog's <code>near</code> distance. As the vertex <code>position</code> gets closer to the end of the fog's <code>far</code> distance, the <code>intensity</code> moves closer to <code>fogMax</code>. For any vertexes after the end of the fog, the <code>intensity</code> is clamped to <code>fogMax</code>.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  fragColor = vec4(color.rgb, intensity);</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Set the fragment's color to the fog <code>color</code> and the fragment's alpha channel to the <code>intensity</code>. As <code>intensity</code> gets closer to one, you'll have less of the scene's color and more of the fog color. When <code>intensity</code> reaches one, you'll have all fog color and nothing else.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>uniform sampler2D baseTexture;</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>uniform sampler2D fogTexture;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>  vec2 texSize  = textureSize(baseTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a></span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a>  vec4 baseColor = texture(baseTexture, texCoord);</span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\"></a>  vec4 fogColor  = texture(fogTexture,  texCoord);</span>\n<span id=\"cb6-13\"><a href=\"#cb6-13\"></a></span>\n<span id=\"cb6-14\"><a href=\"#cb6-14\"></a>  fragColor = baseColor;</span>\n<span id=\"cb6-15\"><a href=\"#cb6-15\"></a></span>\n<span id=\"cb6-16\"><a href=\"#cb6-16\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-17\"><a href=\"#cb6-17\"></a></span>\n<span id=\"cb6-18\"><a href=\"#cb6-18\"></a>  fragColor = mix(fragColor, fogColor, min(fogColor.a, <span class=\"dv\">1</span>));</span>\n<span id=\"cb6-19\"><a href=\"#cb6-19\"></a></span>\n<span id=\"cb6-20\"><a href=\"#cb6-20\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Normally you calculate the fog in the same shader that does the lighting calculations. However, you can also break it out into its own framebuffer texture. Here you see the fog color being applied to the rest of the scene much like you would apply a layer in GIMP. This allows you to calculate the fog once instead calculating it in every shader that eventually needs it.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/fog.frag\" target=\"_blank\" rel=\"noopener noreferrer\">fog.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/outline.frag\" target=\"_blank\" rel=\"noopener noreferrer\">outline.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/scene-combine.frag\" target=\"_blank\" rel=\"noopener noreferrer\">scene-combine.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"deferred-rendering.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"blur.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/fresnel-factor.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Fresnel Factor | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Fresnel Factor | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Fresnel Factor | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"blinn-phong.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"rim-lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"fresnel-factor\">Fresnel Factor</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3lQL51m.gif\" alt=\"Fresnel\" title=\"Fresnel\">\n</p>\n\n<p>The fresnel factor alters the reflectiveness of a surface based on the camera or viewing angle. As a surface points away from the camera, its reflectiveness goes up. Similarly, as a surface points towards the camera, its reflectiveness goes down.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WolRRhX.png\" alt=\"\" title=\"\">\n</p>\n\n<p>In other words, as a surface becomes perpendicular with the camera, it becomes more mirror like. Utilizing this property, you can vary the opacity of reflections (such as <a href=\"lighting.html#specular\">specular</a> and <a href=\"screen-space-reflection.html\">screen space reflections</a>) and/or vary a surface's alpha values for a more plausible or realistic look.</p>\n<h3 id=\"specular-reflection\">Specular Reflection</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FnOhXxv.gif\" alt=\"Specular Intensity\" title=\"Specular Intensity\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  vec4 specular =   materialSpecularColor</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a>                  * lightSpecularColor</span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>                  * pow(max(dot(eye, reflection), <span class=\"fl\">0.0</span>), shininess);</span></code></pre></div>\n<p>In the <a href=\"lighting.html#specular\">lighting</a> section, the specular component was a combination of the material's specular color, the light's specular color, and by how much the camera pointed into the light's reflection direction. Incorporating the fresnel factor, you'll now vary the material specular color based on the angle between the camera and the surface it's pointed at.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  vec3 eye = normalize(-vertexPosition.xyz);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The first vector you'll need is the eye/view/camera vector. Recall that the eye vector points from the vertex position to the camera's position. If the vertex position is in view or camera space, the eye vector is the vertex position pointed in the opposite direction.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  vec3 light   = normal(lightPosition.xyz - vertexPosition.xyz);</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  vec3 halfway = normalize(light + eye);</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fresnel factor is calculated using two vectors. The simplest two vectors to use are the eye and normal vector. However, if you're using the halfway vector (from the <a href=\"blinn-phong.html\">Blinn-Phong</a> section), you can instead calculate the fresnel factor using the halfway and eye vector.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a></span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  <span class=\"dt\">float</span> fresnelFactor = dot(halfway, eye); <span class=\"co\">// Or dot(normal, eye).</span></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>        fresnelFactor = max(fresnelFactor, <span class=\"fl\">0.0</span>);</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>        fresnelFactor = <span class=\"fl\">1.0</span> - fresnelFactor;</span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>        fresnelFactor = pow(fresnelFactor, fresnelPower);</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a></span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>With the needed vectors in hand, you can now compute the fresnel factor. The fresnel factor ranges from zero to one. When the dot product is one, the fresnel factor is zero. When the dot product is less than or equal to zero, the fresnel factor is one. This equation comes from <a href=\"https://en.wikipedia.org/wiki/Schlick%27s_approximation\">Schlick's approximation</a>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AAFI8p1.gif\" alt=\"Fresnel Power\" title=\"Fresnel Power\">\n</p>\n\n<p>In Schlick's approximation, the <code>fresnelPower</code> is five but you can alter this to your liking. The demo code varies it using the blue channel of the specular map with a maximum value of five.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  materialSpecularColor.rgb = mix(materialSpecularColor.rgb, vec3(<span class=\"fl\">1.0</span>), fresnelFactor);</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Once the fresnel factor is determined, use it to modulate the material's specular color. As the fresnel factor approaches one, the material becomes more like a mirror or fully reflective.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  vec4 specular      = vec4(vec3(<span class=\"fl\">0.0</span>), <span class=\"fl\">1.0</span>);</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>       specular.rgb  =   materialSpecularColor.rgb</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>                       * lightSpecularColor.rgb</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>                       * pow</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>                          ( max(dot(normal, halfway), <span class=\"fl\">0.0</span>) <span class=\"co\">// Or max(dot(reflection, eye), 0.0).</span></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>                          , shininess</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>                          );</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a></span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>As before, the specular component is a combination of the material's specular color, the light's specular color, and by how much the camera points into the direction of the light's reflection. However, using the fresnel factor, the material's specular color various depending on the orientation of the camera and the surface it's looking at.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"blinn-phong.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"rim-lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/gamma-correction.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Gamma Correction | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Gamma Correction | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Gamma Correction | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"lookup-table.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"setup.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"gamma-correction\">Gamma Correction</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IG7A6cj.gif\" alt=\"Gamma Correction\" title=\"Gamma Correction\">\n</p>\n\n<p>Correcting for gamma will make your color calculations look correct. This isn't to say they'll look amazing but with gamma correction, you'll find that the colors meld together better, the shadows are more nuanced, and the highlights are more subtle. Without gamma correction, the shadowed areas tend to get crushed while the highlighted areas tend to get blown-out and over saturated making for a harsh contrast overall.</p>\n<p>If you're aiming for realism, gamma correction is especially important. As you perform more and more calculations, the tiny errors add up making it harder to achieve photorealism. The equations will be correct but the inputs and outputs will be wrong leaving you frustrated.</p>\n<p>It's easy to get twisted around when thinking about gamma correction but essentially it boils down to knowing what color space a color is in and how to convert that color to the color space you need. With those two pieces of the puzzle, gamma correction becomes a tedious yet simple chore you'll have to perform from time to time.</p>\n<h3 id=\"color-spaces\">Color Spaces</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/a1U5oBq.png\" alt=\"sRGB vs RGB\" title=\"sRGB vs RGB\">\n</p>\n\n<p>The two color spaces you'll need to be aware of are sRGB (standard Red Green Blue) and RGB or linear color space.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"ex\">identify</span> -format <span class=\"st\">&quot;%[colorspace]\\n&quot;</span> house-diffuse-srgb.png</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a><span class=\"ex\">sRGB</span></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a></span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a><span class=\"ex\">identify</span> -format <span class=\"st\">&quot;%[colorspace]\\n&quot;</span> house-diffuse-rgb.png</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a><span class=\"ex\">RGB</span></span></code></pre></div>\n<p>Knowing what color space a color texture is in will determine how you handle it in your shaders. To determine the color space of a texture, use ImageMagick's <code>identify</code>. You'll find that most textures are in sRGB.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"ex\">convert</span> house-diffuse-srgb -colorspace rgb house-diffuse-rgb.png</span></code></pre></div>\n<p>To convert a texture to a particular color space, use ImageMagick's <code>convert</code> program. Notice how a texture is darkened when transforming from sRGB to RGB.</p>\n<h3 id=\"decoding\">Decoding</h3>\n<p>The red, green, and blue values in a sRGB color texture are encoded and cannot be modified directly. Modifying them directly would be like running spellcheck on an encrypted message. Before you can run spellcheck, you first have to decrypt the message. Similarly, to modify the values of an sRGB texture, you first have to decode or transform them to RGB or linear color space.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  color     = texture(color_texture, uv);</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  color.rgb = pow(color.rgb, <span class=\"fl\">2.2</span>);</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To decode a sRGB encoded color, raise the <code>rgb</code> values to the power of <code>2.2</code>. Once you have decoded the color, you are now free to add, subtract, multiply, and divide it.</p>\n<p>By raising the color values to the power of <code>2.2</code>, you're converting them from sRGB to RGB or linear color space. This conversion has the effect of darkening the colors.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/E5nkRfG.png\" alt=\"Color Darkening\" title=\"Color Darkening\">\n</p>\n\n<p>For example, <code>vec3(0.9, 0.2, 0.3)</code> becomes <code>vec3(0.793, 0.028, 0.07)</code>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/TOEb0EC.gif\" alt=\"Gamma Curves\" title=\"Gamma Curves\">\n</p>\n\n<p>The <code>2.2</code> value is known as gamma. Loosely speaking, gamma can either be <code>1.0 / 2.2</code>, <code>2.2</code>, or <code>1.0</code>. As you've seen, <code>2.2</code> is for decoding sRGB encoded color textures. As you will see, <code>1.0 / 2.2</code> is for encoding linear or RGB color textures. And <code>1.0</code> is RGB or linear color space since <code>y = 1 * x + 0</code> and any base raised to the power of <code>1.0</code> is itself.</p>\n<h4 id=\"non-color-data\">Non-color Data</h4>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/reA2qjs.png\" alt=\"Non-color Data\" title=\"Non-color Data\">\n</p>\n\n<p>One important exception to decoding is when the \"colors\" of a texture represent non-color data. Some examples of non-color data would be the normals in a normal map, the alpha channel, the heights in a height map, and the directions in a flow map. Only decode color related data or data that represents color. When dealing with non-color data, treat the sRGB color values as RGB or linear and skip the decoding process.</p>\n<h3 id=\"encoding\">Encoding</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tRxkKNe.gif\" alt=\"Perceptually versus Actually Linear\" title=\"Perceptually versus Actually Linear\">\n</p>\n\n<p>The necessity for encoding and decoding stems from the fact that humans do not perceive lightness linearly and most displays (like a monitor) lack the precision or number of bits to accurately show both lighter and darker tonal values or shades. With only so many bits to go around, colors are encoded in such a way that more bits are devoted to the darker shades than the lighter shades since humans are more sensitive to darker tones than lighter tones. Encoding it this way uses the limited number of bits more effectively for human perception. Still, the only thing to remember is that your display is expecting sRGB encoded values. Therefore, if you decoded a sRGB value, you have to encode it before it makes its way to your display.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  color     = texture(color_texture, uv);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  color.rgb = pow(color.rgb, <span class=\"fl\">1.0</span> / <span class=\"fl\">2.2</span>);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To encode a linear value or convert RGB to sRGB, raise the <code>rgb</code> values to the power of <code>1.0 / 2.2</code>. Notice how <code>1.1 / 2.2</code> is the reciprocal of <code>2.2</code> or <code>2.2 / 1.0</code>. Here you see the symmetry in decoding and encoding.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4km0pdv.gif\" alt=\"Not Gamma Corrected versus Gamma Corrected\" title=\"Not Gamma Corrected versus Gamma Corrected\">\n</p>\n\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/gamma-correction.frag\" target=\"_blank\" rel=\"noopener noreferrer\">gamma-correction.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"lookup-table.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"setup.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/glsl.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"GLSL | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"GLSL | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>GLSL | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"reference-frames.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"render-to-texture.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"glsl\">GLSL</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7b5MCBG.gif\" alt=\"\" title=\"\">\n</p>\n\n<p>Instead of using the <a href=\"https://en.wikipedia.org/wiki/Fixed-function\">fixed-function</a> pipeline, you'll be using the programmable GPU rendering pipeline. Since it is programmable, it is up to you to supply the programming in the form of shaders. A shader is a (typically small) program you write using a syntax reminiscent of C. The programmable GPU rendering pipeline has various different stages that you can program with shaders. The different types of shaders include vertex, tessellation, geometry, fragment, and compute. You'll only need to focus on the vertex and fragment stages for the techniques below.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a><span class=\"dt\">void</span> main() {}</span></code></pre></div>\n<p>Here is a bare-bones GLSL shader consisting of the GLSL version number and the main function.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform mat4 p3d_ModelViewProjectionMatrix;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>in vec4 p3d_Vertex;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a><span class=\"dt\">void</span> main()</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>{</span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a>}</span></code></pre></div>\n<p>Here is a stripped down GLSL vertex shader that transforms an incoming vertex to clip space and outputs this new position as the vertex's homogeneous position. The <code>main</code> procedure doesn't return anything since it is <code>void</code> and the <code>gl_Position</code> variable is a built-in output.</p>\n<p>Take note of the keywords <code>uniform</code> and <code>in</code>. The <code>uniform</code> keyword means this global variable is the same for all vertexes. Panda3D sets the <code>p3d_ModelViewProjectionMatrix</code> for you and it is the same matrix for each vertex. The <code>in</code> keyword means this global variable is being given to the shader. The vertex shader receives each vertex that makes up the geometry the vertex shader is attached to.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>out vec4 fragColor;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  fragColor = vec4(<span class=\"dv\">0</span>, <span class=\"dv\">1</span>, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>}</span></code></pre></div>\n<p>Here is a stripped down GLSL fragment shader that outputs the fragment color as solid green. Keep in mind that a fragment affects at most one screen pixel but a single pixel can be affected by many fragments.</p>\n<p>Take note of the <code>out</code> keyword. The <code>out</code> keyword means this global variable is being set by the shader. The name <code>fragColor</code> is arbitrary so feel free to choose a different one.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/V25UzMa.gif\" alt=\"Output of the stripped down shaders.\" title=\"Output of the stripped down shaders.\">\n</p>\n\n<p>This is the output of the two shaders shown above.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"reference-frames.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"render-to-texture.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/x8rtGr4.gif\" alt=\"3D Game Shaders For Beginners\" title=\"3D Game Shaders For Beginners\">\n</p>\n\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<p>Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! Below is a collection of shading techniques that will take your game visuals to new heights. I've explained each technique in such a way that you can take what you learn here and apply/port it to whatever stack you use—be it Godot, Unity, Unreal, or something else. For the glue in between the shaders, I've chosen the fabulous Panda3D game engine and the OpenGL Shading Language (GLSL). So if that is your stack, then you'll also get the benefit of learning how to use these shading techniques with Panda3D and OpenGL specifically.</p>\n<h2 id=\"table-of-contents\">Table Of Contents</h2>\n<ul>\n<li><a href=\"setup.html\">Setup</a></li>\n<li><a href=\"building-the-demo.html\">Building The Demo</a></li>\n<li><a href=\"running-the-demo.html\">Running The Demo</a></li>\n<li><a href=\"reference-frames.html\">Reference Frames</a></li>\n<li><a href=\"glsl.html\">GLSL</a></li>\n<li><a href=\"render-to-texture.html\">Render To Texture</a></li>\n<li><a href=\"texturing.html\">Texturing</a></li>\n<li><a href=\"lighting.html\">Lighting</a></li>\n<li><a href=\"blinn-phong.html\">Blinn-Phong</a></li>\n<li><a href=\"fresnel-factor.html\">Fresnel Factor</a></li>\n<li><a href=\"rim-lighting.html\">Rim Lighting</a></li>\n<li><a href=\"cel-shading.html\">Cel Shading</a></li>\n<li><a href=\"normal-mapping.html\">Normal Mapping</a></li>\n<li><a href=\"deferred-rendering.html\">Deferred Rendering</a></li>\n<li><a href=\"fog.html\">Fog</a></li>\n<li><a href=\"blur.html\">Blur</a></li>\n<li><a href=\"bloom.html\">Bloom</a></li>\n<li><a href=\"ssao.html\">SSAO</a></li>\n<li><a href=\"motion-blur.html\">Motion Blur</a></li>\n<li><a href=\"chromatic-aberration.html\">Chromatic Aberration</a></li>\n<li><a href=\"screen-space-reflection.html\">Screen Space Reflection</a></li>\n<li><a href=\"screen-space-refraction.html\">Screen Space Refraction</a></li>\n<li><a href=\"foam.html\">Foam</a></li>\n<li><a href=\"flow-mapping.html\">Flow Mapping</a></li>\n<li><a href=\"outlining.html\">Outlining</a></li>\n<li><a href=\"depth-of-field.html\">Depth Of Field</a></li>\n<li><a href=\"posterization.html\">Posterization</a></li>\n<li><a href=\"pixelization.html\">Pixelization</a></li>\n<li><a href=\"sharpen.html\">Sharpen</a></li>\n<li><a href=\"dilation.html\">Dilation</a></li>\n<li><a href=\"film-grain.html\">Film Grain</a></li>\n<li><a href=\"lookup-table.html\">Lookup Table (LUT)</a></li>\n<li><a href=\"gamma-correction.html\">Gamma Correction</a></li>\n</ul>\n<h2 id=\"license\">License</h2>\n<p>The included license applies only to the software portion of 3D Game Shaders For Beginners— specifically the <code>.cxx</code>, <code>.vert</code>, and <code>.frag</code> source code files. No other portion of 3D Game Shaders For Beginners has been licensed for use.</p>\n<h2 id=\"attributions\">Attributions</h2>\n<ul>\n<li><a href=\"https://fontenddev.com/fonts/kiwi-soda/\">Kiwi Soda Font</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/lighting.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Lighting | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Lighting | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Lighting | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"texturing.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"blinn-phong.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"lighting\">Lighting</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/zQrA8tr.gif\" alt=\"Lighting\" title=\"Lighting\">\n</p>\n\n<p>Completing the lighting involves calculating and combining the ambient, diffuse, specular, and emission light aspects. The example code uses either Phong or Blinn-Phong lighting.</p>\n<h3 id=\"vertex\">Vertex</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform <span class=\"kw\">struct</span> p3d_LightSourceParameters</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  { vec4 color</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a></span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>  ; vec4 ambient</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>  ; vec4 diffuse</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a>  ; vec4 specular</span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a></span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a>  ; vec4 position</span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a></span>\n<span id=\"cb1-12\"><a href=\"#cb1-12\"></a>  ; vec3  spotDirection</span>\n<span id=\"cb1-13\"><a href=\"#cb1-13\"></a>  ; <span class=\"dt\">float</span> spotExponent</span>\n<span id=\"cb1-14\"><a href=\"#cb1-14\"></a>  ; <span class=\"dt\">float</span> spotCutoff</span>\n<span id=\"cb1-15\"><a href=\"#cb1-15\"></a>  ; <span class=\"dt\">float</span> spotCosCutoff</span>\n<span id=\"cb1-16\"><a href=\"#cb1-16\"></a></span>\n<span id=\"cb1-17\"><a href=\"#cb1-17\"></a>  ; <span class=\"dt\">float</span> constantAttenuation</span>\n<span id=\"cb1-18\"><a href=\"#cb1-18\"></a>  ; <span class=\"dt\">float</span> linearAttenuation</span>\n<span id=\"cb1-19\"><a href=\"#cb1-19\"></a>  ; <span class=\"dt\">float</span> quadraticAttenuation</span>\n<span id=\"cb1-20\"><a href=\"#cb1-20\"></a></span>\n<span id=\"cb1-21\"><a href=\"#cb1-21\"></a>  ; vec3 attenuation</span>\n<span id=\"cb1-22\"><a href=\"#cb1-22\"></a></span>\n<span id=\"cb1-23\"><a href=\"#cb1-23\"></a>  ; sampler2DShadow shadowMap</span>\n<span id=\"cb1-24\"><a href=\"#cb1-24\"></a></span>\n<span id=\"cb1-25\"><a href=\"#cb1-25\"></a>  ; mat4 shadowViewMatrix</span>\n<span id=\"cb1-26\"><a href=\"#cb1-26\"></a>  ;</span>\n<span id=\"cb1-27\"><a href=\"#cb1-27\"></a>  } p3d_LightSource[NUMBER_OF_LIGHTS];</span>\n<span id=\"cb1-28\"><a href=\"#cb1-28\"></a></span>\n<span id=\"cb1-29\"><a href=\"#cb1-29\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>For every light, minus the ambient light, Panda3D gives you this convenient struct which is available to both the vertex and fragment shaders. The biggest convenience being the shadow map and shadow view matrix for transforming vertexes to shadow or light space.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  vertexPosition = p3d_ModelViewMatrix * p3d_Vertex;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; p3d_LightSource.length(); ++i) {</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>    vertexInShadowSpaces[i] = p3d_LightSource[i].shadowViewMatrix * vertexPosition;</span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>  }</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a></span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Starting in the vertex shader, you'll need to transform and output the vertex from view space to shadow or light space for each light in your scene. You'll need this later in the fragment shader in order to render the shadows. Shadow or light space is where every coordinate is relative to the light position (the light is the origin).</p>\n<h3 id=\"fragment\">Fragment</h3>\n<p>The fragment shader is where most of the lighting calculations take place.</p>\n<h4 id=\"material\">Material</h4>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform <span class=\"kw\">struct</span></span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  { vec4 ambient</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  ; vec4 diffuse</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  ; vec4 emission</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  ; vec3 specular</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  ; <span class=\"dt\">float</span> shininess</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  ;</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  } p3d_Material;</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Panda3D gives us the material (in the form of a struct) for the mesh or model you are currently rendering.</p>\n<h4 id=\"multiple-lights\">Multiple Lights</h4>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec4 diffuse  = vec4(<span class=\"fl\">0.0</span>, <span class=\"fl\">0.0</span>, <span class=\"fl\">0.0</span>, diffuseTex.a);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec4 specular = vec4(<span class=\"fl\">0.0</span>, <span class=\"fl\">0.0</span>, <span class=\"fl\">0.0</span>, diffuseTex.a);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Before you loop through the scene's lights, create an accumulator for both the diffuse and specular colors.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; p3d_LightSource.length(); ++i) {</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  }</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a></span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now you can loop through the lights, calculating the diffuse and specular colors for each one.</p>\n<h4 id=\"light-related-vectors\">Light Related Vectors</h4>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/0pzNh5d.gif\" alt=\"Phong Lighting Model\" title=\"Phong Lighting Model\">\n</p>\n\n<p>Here you see the four major vectors you'll need to calculate the diffuse and specular colors contributed by each light. The light direction vector is the light blue arrow pointing to the light. The normal vector is the green arrow standing straight up. The reflection vector is the dark blue arrow mirroring the light direction vector. The eye or view vector is the orange arrow pointing towards the camera.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>    vec3 lightDirection =</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>        p3d_LightSource[i].position.xyz</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>      - vertexPosition.xyz</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>      * p3d_LightSource[i].position.w;</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The light direction is from the vertex's position to the light's position.</p>\n<p>Panda3D sets <code>p3d_LightSource[i].position.w</code> to zero if this is a directional light. Directional lights do not have a position as they only have a direction. So if this is a directional light, the light direction will be the negative or opposite direction of the light as Panda3D sets <code>p3d_LightSource[i].position.xyz</code> to be <code>-direction</code> for directional lights.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  normal = normalize(vertexNormal);</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You'll need the vertex normal to be a unit vector. Unit vectors have a length of magnitude of one.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>    vec3 unitLightDirection = normalize(lightDirection);</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>    vec3 eyeDirection       = normalize(-vertexPosition.xyz);</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>    vec3 reflectedDirection = normalize(-reflect(unitLightDirection, normal));</span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a></span>\n<span id=\"cb8-7\"><a href=\"#cb8-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Next you'll need three more vectors.</p>\n<p>You'll need to take the dot product involving the light direction so its best to normalize it. This gives it a distance or magnitude of one (unit vector).</p>\n<p>The eye direction is the opposite of the vertex/fragment position since the vertex/fragment position is relative to the camera's position. Remember that the vertex/fragment position is in view space. So instead of going from the camera (eye) to the vertex/fragment, you go from the vertex/fragment to the eye (camera).</p>\n<p>The <a href=\"http://asawicki.info/news_1301_reflect_and_refract_functions.html\">reflection vector</a> is a reflection of the light direction at the surface normal. As the light \"ray\" hits the surface, it bounces off at the same angle it came in at. The angle between the light direction vector and the normal is known as the \"angle of incidence\". The angle between the reflection vector and the normal is known as the \"angle of reflection\".</p>\n<p>You'll have to negate the reflected light vector as it needs to point in the same direction as the eye vector. Remember the eye direction is from the vertex/fragment to the camera position. You'll use the reflection vector to calculate the intensity of the specular highlight.</p>\n<h4 id=\"diffuse\">Diffuse</h4>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>    <span class=\"dt\">float</span> diffuseIntensity  = dot(normal, unitLightDirection);</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a></span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>    <span class=\"cf\">if</span> (diffuseIntensity &lt; <span class=\"fl\">0.0</span>) { <span class=\"cf\">continue</span>; }</span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\"></a></span>\n<span id=\"cb9-7\"><a href=\"#cb9-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The diffuse intensity is the dot product between the surface normal and the unit vector light direction. The dot product can range from negative one to one. If both vectors point in the same direction, the intensity is one. Any other case will be less than one.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Nb78z96.gif\" alt=\"The light direction versus the normal direction.\" title=\"The light direction versus the normal direction.\">\n</p>\n\n<p>As the light vector approaches the same direction as the normal, the diffuse intensity approaches one.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>    <span class=\"cf\">if</span> (diffuseIntensity &lt; <span class=\"fl\">0.0</span>) { <span class=\"cf\">continue</span>; }</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a></span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the diffuse intensity is zero or less, move on to the next light.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>    vec4 diffuseTemp =</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a>      vec4</span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a>        ( clamp</span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a>            (   diffuseTex.rgb</span>\n<span id=\"cb11-7\"><a href=\"#cb11-7\"></a>              * p3d_LightSource[i].diffuse.rgb</span>\n<span id=\"cb11-8\"><a href=\"#cb11-8\"></a>              * diffuseIntensity</span>\n<span id=\"cb11-9\"><a href=\"#cb11-9\"></a>            , <span class=\"dv\">0</span></span>\n<span id=\"cb11-10\"><a href=\"#cb11-10\"></a>            , <span class=\"dv\">1</span></span>\n<span id=\"cb11-11\"><a href=\"#cb11-11\"></a>            )</span>\n<span id=\"cb11-12\"><a href=\"#cb11-12\"></a>        , diffuseTex.a</span>\n<span id=\"cb11-13\"><a href=\"#cb11-13\"></a>        );</span>\n<span id=\"cb11-14\"><a href=\"#cb11-14\"></a></span>\n<span id=\"cb11-15\"><a href=\"#cb11-15\"></a>    diffuseTemp = clamp(diffuseTemp, vec4(<span class=\"dv\">0</span>), diffuseTex);</span>\n<span id=\"cb11-16\"><a href=\"#cb11-16\"></a></span>\n<span id=\"cb11-17\"><a href=\"#cb11-17\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You can now calculate the diffuse color contributed by this light. If the diffuse intensity is one, the diffuse color will be a mix between the diffuse texture color and the lights color. Any other intensity will cause the diffuse color to be darker.</p>\n<p>Notice how I clamp the diffuse color to be only as bright as the diffuse texture color is. This will protect the scene from being over exposed. When creating your diffuse textures, make sure to create them as if they were fully lit.</p>\n<h4 id=\"specular\">Specular</h4>\n<p>After diffuse, comes specular.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FnOhXxv.gif\" alt=\"Specular Intensity\" title=\"Specular Intensity\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>    <span class=\"dt\">float</span> specularIntensity = max(dot(reflectedDirection, eyeDirection), <span class=\"dv\">0</span>);</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a></span>\n<span id=\"cb12-5\"><a href=\"#cb12-5\"></a>    vec4 specularTemp =</span>\n<span id=\"cb12-6\"><a href=\"#cb12-6\"></a>      clamp</span>\n<span id=\"cb12-7\"><a href=\"#cb12-7\"></a>        (   vec4(p3d_Material.specular, <span class=\"dv\">1</span>)</span>\n<span id=\"cb12-8\"><a href=\"#cb12-8\"></a>          * p3d_LightSource[i].specular</span>\n<span id=\"cb12-9\"><a href=\"#cb12-9\"></a>          * pow</span>\n<span id=\"cb12-10\"><a href=\"#cb12-10\"></a>              ( specularIntensity</span>\n<span id=\"cb12-11\"><a href=\"#cb12-11\"></a>              , p3d_Material.shininess</span>\n<span id=\"cb12-12\"><a href=\"#cb12-12\"></a>              )</span>\n<span id=\"cb12-13\"><a href=\"#cb12-13\"></a>        , <span class=\"dv\">0</span></span>\n<span id=\"cb12-14\"><a href=\"#cb12-14\"></a>        , <span class=\"dv\">1</span></span>\n<span id=\"cb12-15\"><a href=\"#cb12-15\"></a>        );</span>\n<span id=\"cb12-16\"><a href=\"#cb12-16\"></a></span>\n<span id=\"cb12-17\"><a href=\"#cb12-17\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The specular intensity is the dot product between the eye vector and the reflection vector. As with the diffuse intensity, if the two vectors point in the same direction, the specular intensity is one. Any other intensity will diminish the amount of specular color contributed by this light.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4r6wqLP.gif\" alt=\"Shininess\" title=\"Shininess\">\n</p>\n\n<p>The material shininess determines how spread out the specular highlight is. This is typically set in a modeling program like Blender. In Blender it's known as the specular hardness.</p>\n<h4 id=\"spotlights\">Spotlights</h4>\n<div class=\"sourceCode\" id=\"cb13\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb13-2\"><a href=\"#cb13-2\"></a></span>\n<span id=\"cb13-3\"><a href=\"#cb13-3\"></a>    <span class=\"dt\">float</span> unitLightDirectionDelta =</span>\n<span id=\"cb13-4\"><a href=\"#cb13-4\"></a>      dot</span>\n<span id=\"cb13-5\"><a href=\"#cb13-5\"></a>        ( normalize(p3d_LightSource[i].spotDirection)</span>\n<span id=\"cb13-6\"><a href=\"#cb13-6\"></a>        , -unitLightDirection</span>\n<span id=\"cb13-7\"><a href=\"#cb13-7\"></a>        );</span>\n<span id=\"cb13-8\"><a href=\"#cb13-8\"></a></span>\n<span id=\"cb13-9\"><a href=\"#cb13-9\"></a>    <span class=\"cf\">if</span> (unitLightDirectionDelta &lt; p3d_LightSource[i].spotCosCutoff) { <span class=\"cf\">continue</span>; }</span>\n<span id=\"cb13-10\"><a href=\"#cb13-10\"></a></span>\n<span id=\"cb13-11\"><a href=\"#cb13-11\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb13-12\"><a href=\"#cb13-12\"></a>}</span></code></pre></div>\n<p>This snippet keeps fragments outside of a spotlight's cone or frustum from being affected by the light. Fortunately, Panda3D <a href=\"https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L1705\">sets up</a> <code>spotDirection</code> and <code>spotCosCutoff</code> to also work for directional lights and points lights. Spotlights have both a position and direction. However, directional lights only have a direction and point lights only have a position. Still, this code works for all three lights avoiding the need for noisy if statements.</p>\n<div class=\"sourceCode\" id=\"cb14\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb14-1\"><a href=\"#cb14-1\"></a>        <span class=\"co\">// ...</span></span>\n<span id=\"cb14-2\"><a href=\"#cb14-2\"></a></span>\n<span id=\"cb14-3\"><a href=\"#cb14-3\"></a>        , -unitLightDirection</span>\n<span id=\"cb14-4\"><a href=\"#cb14-4\"></a></span>\n<span id=\"cb14-5\"><a href=\"#cb14-5\"></a>        <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You must negate <code>unitLightDirection</code>. <code>unitLightDirection</code> goes from the fragment to the spotlight and you need it to go from the spotlight to the fragment since the <code>spotDirection</code> goes directly down the center of the spotlight's frustum some distance away from the spotlight's position.</p>\n<div class=\"sourceCode\" id=\"cb15\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb15-1\"><a href=\"#cb15-1\"></a>spotCosCutoff = cosine(<span class=\"fl\">0.5</span> * spotlightLensFovAngle);</span></code></pre></div>\n<p>For a spotlight, if the dot product between the fragment-to-light vector and the spotlight's direction vector is less than the cosine of half the spotlight's field of view angle, the shader disregards this light's influence.</p>\n<p>For directional lights and point lights, Panda3D sets <code>spotCosCutoff</code> to negative one. Recall that the dot product ranges from negative one to one. So it doesn't matter what the <code>unitLightDirectionDelta</code> is because it will always be greater than or equal to negative one.</p>\n<div class=\"sourceCode\" id=\"cb16\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb16-1\"><a href=\"#cb16-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb16-2\"><a href=\"#cb16-2\"></a></span>\n<span id=\"cb16-3\"><a href=\"#cb16-3\"></a>    diffuseTemp *= pow(unitLightDirectionDelta, p3d_LightSource[i].spotExponent);</span>\n<span id=\"cb16-4\"><a href=\"#cb16-4\"></a></span>\n<span id=\"cb16-5\"><a href=\"#cb16-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like the <code>unitLightDirectionDelta</code> snippet, this snippet also works for all three light types. For spotlights, this will make the fragments brighter as you move closer to the center of the spotlight's frustum. For directional lights and point lights, <code>spotExponent</code> is zero. Recall that anything to the power of zero is one so the diffuse color is one times itself meaning it is unchanged.</p>\n<h4 id=\"shadows\">Shadows</h4>\n<div class=\"sourceCode\" id=\"cb17\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb17-1\"><a href=\"#cb17-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb17-2\"><a href=\"#cb17-2\"></a></span>\n<span id=\"cb17-3\"><a href=\"#cb17-3\"></a>    <span class=\"dt\">float</span> shadow =</span>\n<span id=\"cb17-4\"><a href=\"#cb17-4\"></a>      textureProj</span>\n<span id=\"cb17-5\"><a href=\"#cb17-5\"></a>        ( p3d_LightSource[i].shadowMap</span>\n<span id=\"cb17-6\"><a href=\"#cb17-6\"></a>        , vertexInShadowSpaces[i]</span>\n<span id=\"cb17-7\"><a href=\"#cb17-7\"></a>        );</span>\n<span id=\"cb17-8\"><a href=\"#cb17-8\"></a></span>\n<span id=\"cb17-9\"><a href=\"#cb17-9\"></a>    diffuseTemp.rgb  *= shadow;</span>\n<span id=\"cb17-10\"><a href=\"#cb17-10\"></a>    specularTemp.rgb *= shadow;</span>\n<span id=\"cb17-11\"><a href=\"#cb17-11\"></a></span>\n<span id=\"cb17-12\"><a href=\"#cb17-12\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Panda3D makes applying shadows relatively easy by providing the shadow map and shadow transformation matrix for every scene light. To create the shadow transformation matrix yourself, you'll need to assemble a matrix that transforms view space coordinates to light space (coordinates are relative to the light's position). To create the shadow map yourself, you'll need to render the scene from the perspective of the light to a framebuffer texture. The framebuffer texture must hold the distances from the light to the fragments. This is known as a \"depth map\". Lastly, you'll need to manually give to your shader your DIY depth map as a <code>uniform sampler2DShadow</code> and your DIY shadow transformation matrix as a <code>uniform mat4</code>. At this point, you've recreated what Panda3D does for you automatically.</p>\n<p>The shadow snippet shown uses <code>textureProj</code> which is different from the <code>texure</code> function shown earlier. <code>textureProj</code> first divides <code>vertexInShadowSpaces[i].xyz</code> by <code>vertexInShadowSpaces[i].w</code>. After this, it uses <code>vertexInShadowSpaces[i].xy</code> to locate the depth stored in the shadow map. Next it uses <code>vertexInShadowSpaces[i].z</code> to compare this vertex's depth against the shadow map depth at <code>vertexInShadowSpaces[i].xy</code>. If the comparison passes, <code>textureProj</code> will return one. Otherwise, it will return zero. Zero meaning this vertex/fragment is in the shadow and one meaning this vertex/fragment is not in the shadow.</p>\n<p><code>textureProj</code> can also return a value between zero and one depending on how the shadow map was set up. In this instance, <code>textureProj</code> performs multiple depth tests using neighboring depth values and returns a weighted average. This weighted average can give shadows a softer look.</p>\n<h4 id=\"attenuation\">Attenuation</h4>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/jyatr7l.png\" alt=\"Attenuation\" title=\"Attenuation\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb18\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb18-1\"><a href=\"#cb18-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb18-2\"><a href=\"#cb18-2\"></a></span>\n<span id=\"cb18-3\"><a href=\"#cb18-3\"></a>    <span class=\"dt\">float</span> lightDistance = length(lightDirection);</span>\n<span id=\"cb18-4\"><a href=\"#cb18-4\"></a></span>\n<span id=\"cb18-5\"><a href=\"#cb18-5\"></a>    <span class=\"dt\">float</span> attenuation =</span>\n<span id=\"cb18-6\"><a href=\"#cb18-6\"></a>        <span class=\"dv\">1</span></span>\n<span id=\"cb18-7\"><a href=\"#cb18-7\"></a>      / ( p3d_LightSource[i].constantAttenuation</span>\n<span id=\"cb18-8\"><a href=\"#cb18-8\"></a>        + p3d_LightSource[i].linearAttenuation</span>\n<span id=\"cb18-9\"><a href=\"#cb18-9\"></a>        * lightDistance</span>\n<span id=\"cb18-10\"><a href=\"#cb18-10\"></a>        + p3d_LightSource[i].quadraticAttenuation</span>\n<span id=\"cb18-11\"><a href=\"#cb18-11\"></a>        * (lightDistance * lightDistance)</span>\n<span id=\"cb18-12\"><a href=\"#cb18-12\"></a>        );</span>\n<span id=\"cb18-13\"><a href=\"#cb18-13\"></a></span>\n<span id=\"cb18-14\"><a href=\"#cb18-14\"></a>    diffuseTemp.rgb  *= attenuation;</span>\n<span id=\"cb18-15\"><a href=\"#cb18-15\"></a>    specularTemp.rgb *= attenuation;</span>\n<span id=\"cb18-16\"><a href=\"#cb18-16\"></a></span>\n<span id=\"cb18-17\"><a href=\"#cb18-17\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The light's distance is just the magnitude or length of the light direction vector. Notice it's not using the normalized light direction as that distance would be one.</p>\n<p>You'll need the light distance to calculate the attenuation. Attenuation meaning the light's influence diminishes as you get further away from it.</p>\n<p>You can set <code>constantAttenuation</code>, <code>linearAttenuation</code>, and <code>quadraticAttenuation</code> to whatever values you would like. A good starting point is <code>constantAttenuation = 1</code>, <code>linearAttenuation = 0</code>, and <code>quadraticAttenuation = 1</code>. With these settings, the attenuation is one at the light's position and approaches zero as you move further away.</p>\n<h4 id=\"final-light-color\">Final Light Color</h4>\n<div class=\"sourceCode\" id=\"cb19\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb19-1\"><a href=\"#cb19-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb19-2\"><a href=\"#cb19-2\"></a></span>\n<span id=\"cb19-3\"><a href=\"#cb19-3\"></a>    diffuse  += diffuseTemp;</span>\n<span id=\"cb19-4\"><a href=\"#cb19-4\"></a>    specular += specularTemp;</span>\n<span id=\"cb19-5\"><a href=\"#cb19-5\"></a></span>\n<span id=\"cb19-6\"><a href=\"#cb19-6\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To calculate the final light color, add the diffuse and specular together. Be sure to add this to the accumulator as you loop through the scene's lights.</p>\n<h4 id=\"ambient\">Ambient</h4>\n<div class=\"sourceCode\" id=\"cb20\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb20-1\"><a href=\"#cb20-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb20-2\"><a href=\"#cb20-2\"></a></span>\n<span id=\"cb20-3\"><a href=\"#cb20-3\"></a>uniform sampler2D p3d_Texture1;</span>\n<span id=\"cb20-4\"><a href=\"#cb20-4\"></a></span>\n<span id=\"cb20-5\"><a href=\"#cb20-5\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb20-6\"><a href=\"#cb20-6\"></a></span>\n<span id=\"cb20-7\"><a href=\"#cb20-7\"></a>uniform <span class=\"kw\">struct</span></span>\n<span id=\"cb20-8\"><a href=\"#cb20-8\"></a>  { vec4 ambient</span>\n<span id=\"cb20-9\"><a href=\"#cb20-9\"></a>  ;</span>\n<span id=\"cb20-10\"><a href=\"#cb20-10\"></a>  } p3d_LightModel;</span>\n<span id=\"cb20-11\"><a href=\"#cb20-11\"></a></span>\n<span id=\"cb20-12\"><a href=\"#cb20-12\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb20-13\"><a href=\"#cb20-13\"></a></span>\n<span id=\"cb20-14\"><a href=\"#cb20-14\"></a>in vec2 diffuseCoord;</span>\n<span id=\"cb20-15\"><a href=\"#cb20-15\"></a></span>\n<span id=\"cb20-16\"><a href=\"#cb20-16\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb20-17\"><a href=\"#cb20-17\"></a></span>\n<span id=\"cb20-18\"><a href=\"#cb20-18\"></a>  vec4 diffuseTex  = texture(p3d_Texture1, diffuseCoord);</span>\n<span id=\"cb20-19\"><a href=\"#cb20-19\"></a></span>\n<span id=\"cb20-20\"><a href=\"#cb20-20\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb20-21\"><a href=\"#cb20-21\"></a></span>\n<span id=\"cb20-22\"><a href=\"#cb20-22\"></a>  vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex;</span>\n<span id=\"cb20-23\"><a href=\"#cb20-23\"></a></span>\n<span id=\"cb20-24\"><a href=\"#cb20-24\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The ambient component to the lighting model is based on the material's ambient color, the ambient light's color, and the diffuse texture color.</p>\n<p>There should only ever be one ambient light. Because of this, the ambient color calculation only needs to occur once. Contrast this with the diffuse and specular color which must be accumulated for each spot/directional/point light. When you reach <a href=\"ssao.html\">SSAO</a>, you'll revisit the ambient color calculation.</p>\n<h4 id=\"putting-it-all-together\">Putting It All Together</h4>\n<div class=\"sourceCode\" id=\"cb21\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb21-1\"><a href=\"#cb21-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb21-2\"><a href=\"#cb21-2\"></a></span>\n<span id=\"cb21-3\"><a href=\"#cb21-3\"></a>  vec4 outputColor = ambient + diffuse + specular + p3d_Material.emission;</span>\n<span id=\"cb21-4\"><a href=\"#cb21-4\"></a></span>\n<span id=\"cb21-5\"><a href=\"#cb21-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The final color is the sum of the ambient color, diffuse color, specular color, and the emission color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"texturing.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"blinn-phong.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/lookup-table.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Lookup Table | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Lookup Table | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Lookup Table | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"film-grain.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"gamma-correction.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"lookup-table-lut\">Lookup Table (LUT)</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WrPzVlW.gif\" alt=\"LUT\" title=\"LUT\">\n</p>\n\n<p>The lookup table or LUT shader allows you to transform the colors of your game using an image editor like the <a href=\"https://www.gimp.org/\">GIMP</a>. From color grading to turning day into night, the LUT shader is a handy tool for tweaking the look of your game.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/NPdJNGj.png\" alt=\"Neutral LUT\" title=\"Neutral LUT\">\n</p>\n\n<p>Before you can get started, you'll need to find a neutral LUT image. Neutral meaning that it leaves the fragment colors unchanged. The LUT needs to be 256 pixels wide by 16 pixels tall and contain 16 blocks with each block being 16 by 16 pixels.</p>\n<p>The LUT is mapped out into 16 blocks. Each block has a different level of blue. As you move across the blocks, from left to right, the amount of blue increases. You can see the amount of blue in each block's upper-left corner. Within each block, the amount of red increases as you move from left to right and the amount of green increases as you move from top to bottom. The upper-left corner of the first block is black since every RGB channel is zero. The lower-right corner of the last block is white since every RGB channel is one.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/KyxPm1r.png\" alt=\"LUT And Screenshot\" title=\"LUT And Screenshot\">\n</p>\n\n<p>With the neutral LUT in hand, take a screenshot of your game and open it in your image editor. Add the neutral LUT as a new layer and merge it with the screenshot. As you manipulate the colors of the screenshot, the LUT will be altered in the same way. When you're done editing, select only the LUT and save it as a new image. You now have your new lookup table and can begin writing your shader.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  vec2 texSize = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  vec4 color = texture(colorTexture, gl_FragCoord.xy / texSize);</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The LUT shader is a screen space technique. Therefore, sample the scene's color at the current fragment or screen position.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> u  =  floor(color.b * <span class=\"fl\">15.0</span>) / <span class=\"fl\">15.0</span> * <span class=\"fl\">240.0</span>;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>        u  = (floor(color.r * <span class=\"fl\">15.0</span>) / <span class=\"fl\">15.0</span> *  <span class=\"fl\">15.0</span>) + u;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>        u /= <span class=\"fl\">255.0</span>;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  <span class=\"dt\">float</span> v  = ceil(color.g * <span class=\"fl\">15.0</span>);</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>        v /= <span class=\"fl\">15.0</span>;</span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>        v  = <span class=\"fl\">1.0</span> - v;</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a></span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>In order to transform the current fragment's color, using the LUT, you'll need to map the color to two UV coordinates on the lookup table texture. The first mapping (shown up above) is to the nearest left or lower bound block location and the second mapping (shown below) is to the nearest right or upper bound block mapping. At the end, you'll combine these two mappings to create the final color transformation.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/j2JmyQ2.png\" alt=\"RGB Channel Mapping\" title=\"RGB Channel Mapping\">\n</p>\n\n<p>Each of the red, green, and blue channels maps to one of 16 possibilities in the LUT. The blue channel maps to one of the 16 upper-left block corners. After the blue channel maps to a block, the red channel maps to one of the 16 horizontal pixel positions within the block and the green channel maps to one of the 16 vertical pixel positions within the block. These three mappings will determine the UV coordinate you'll need to sample a color from the LUT.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>        u /= <span class=\"fl\">255.0</span>;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>        v /= <span class=\"fl\">15.0</span>;</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>        v  = <span class=\"fl\">1.0</span> - v;</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a></span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To calculate the final U coordinate, divide it by 255 since the LUT is 256 pixels wide and U ranges from zero to one. To calculate the final V coordinate, divide it by 15 since the LUT is 16 pixels tall and V ranges from zero to one. You'll also need to subtract the normalized V coordinate from one since V ranges from zero at the bottom to one at the top while the green channel ranges from zero at the top to 15 at the bottom.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec3 left = texture(lookupTableTexture, vec2(u, v)).rgb;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the UV coordinates, sample a color from the lookup table. This is the nearest left block color.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  u  =  ceil(color.b * <span class=\"fl\">15.0</span>) / <span class=\"fl\">15.0</span> * <span class=\"fl\">240.0</span>;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  u  = (ceil(color.r * <span class=\"fl\">15.0</span>) / <span class=\"fl\">15.0</span> *  <span class=\"fl\">15.0</span>) + u;</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  u /= <span class=\"fl\">255.0</span>;</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a></span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>  v  = <span class=\"fl\">1.0</span> - (ceil(color.g * <span class=\"fl\">15.0</span>) / <span class=\"fl\">15.0</span>);</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a></span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>  vec3 right = texture(lookupTableTexture, vec2(u, v)).rgb;</span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a></span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now you'll need to calculate the UV coordinates for the nearest right block color. Notice how <code>ceil</code> or ceiling is being used now instead of <code>floor</code>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uciq7Um.png\" alt=\"Mixing\" title=\"Mixing\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  color.r = mix(left.r, right.r, fract(color.r * <span class=\"fl\">15.0</span>));</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>  color.g = mix(left.g, right.g, fract(color.g * <span class=\"fl\">15.0</span>));</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>  color.b = mix(left.b, right.b, fract(color.b * <span class=\"fl\">15.0</span>));</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a></span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Not every channel will map perfectly to one of its 16 possibilities. For example, <code>0.5</code> doesn't map perfectly. At the lower bound (<code>floor</code>), it maps to <code>0.4666666666666667</code> and at the upper bound (<code>ceil</code>), it maps to <code>0.5333333333333333</code>. Compare that with <code>0.4</code> which maps to <code>0.4</code> at the lower bound and <code>0.4</code> at the upper bound. For those channels which do not map perfectly, you'll need to mix the left and right sides based on where the channel falls between its lower and upper bound. For <code>0.5</code>, it falls directly between them making the final color a mixture of half left and half right. However, for <code>0.132</code> the mixture will be 98% right and 2% left since the fractional part of <code>0.123</code> times <code>15.0</code> is <code>0.98</code>.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  fragColor = color;</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Set the fragment color to the final mix and you're done.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/lookup-table.frag\" target=\"_blank\" rel=\"noopener noreferrer\">lookup-table.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"film-grain.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"gamma-correction.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/motion-blur.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Motion Blur | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Motion Blur | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Motion Blur | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"ssao.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"chromatic-aberration.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"motion-blur\">Motion Blur</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/eTnhpLr.gif\" alt=\"Motion Blur\" title=\"Motion Blur\">\n</p>\n\n<p>To really sell the illusion of speed, you can do no better than motion blur. From high speed car chases to moving at warp speed, motion blur greatly improves the look and feel of fast moving objects.</p>\n<p>There are a few ways to implement motion blur as a screen space technique. The less involved implementation will only blur the scene in relation to the camera's movements while the more involved version will blur any moving objects even with the camera remaining still. The less involved technique is described below but the principle is the same.</p>\n<h3 id=\"textures\">Textures</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>uniform sampler2D positionTexture;</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a>uniform sampler2D colorTexture;</span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a></span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The input textures needed are the vertex positions in view space and the scene's colors. Refer back to <a href=\"ssao.html#vertex-positions\">SSAO</a> for acquiring the vertex positions.</p>\n<h3 id=\"matrices\">Matrices</h3>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform mat4 previousViewWorldMat;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>uniform mat4 worldViewMat;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>uniform mat4 lensProjection;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The motion blur technique determines the blur direction by comparing the previous frame's vertex positions with the current frame's vertex positions. To do this, you'll need the previous frame's view-to-world matrix, the current frame's world-to-view matrix, and the camera lens' projection matrix.</p>\n<h3 id=\"parameters\">Parameters</h3>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform vec2 parameters;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  <span class=\"dt\">int</span>   size       = <span class=\"dt\">int</span>(parameters.x);</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  <span class=\"dt\">float</span> separation =     parameters.y;</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a></span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The adjustable parameters are <code>size</code> and <code>separation</code>. <code>size</code> controls how many samples are taken along the blur direction. Increasing <code>size</code> increases the amount of blur at the cost of performance. <code>separation</code> controls how spread out the samples are along the blur direction. Increasing <code>separation</code> increases the amount of blur at the cost of accuracy.</p>\n<h3 id=\"blur-direction\">Blur Direction</h3>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  vec4 position1 = texture(positionTexture, texCoord);</span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>  vec4 position0 = worldViewMat * previousViewWorldMat * position1;</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a></span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To determine which way to blur this fragment, you'll need to know where things were last frame and where things are this frame. To figure out where things are now, sample the current vertex position. To figure out where things were last frame, transform the current position from view space to world space, using the previous frame's view-to-world matrix, and then transform it back to view space from world space using this frame's world-to-view matrix. This transformed position is this fragment's previous interpolated vertex position.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/oQqdxM9.gif\" alt=\"Position Projection\" title=\"Position Projection\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  position0      = lensProjection * position0;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  position0.xyz /= position0.w;</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  position0.xy   = position0.xy * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a></span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>  position1      = lensProjection * position1;</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>  position1.xyz /= position1.w;</span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>  position1.xy   = position1.xy * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a></span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now that you have the current and previous positions, transform them to screen space. With the positions in screen space, you can trace out the 2D direction you'll need to blur the onscreen image.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  <span class=\"co\">//   position1.xy = position0.xy + direction;</span></span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>  vec2 direction    = position1.xy - position0.xy;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  <span class=\"cf\">if</span> (length(direction) &lt;= <span class=\"fl\">0.0</span>) { <span class=\"cf\">return</span>; }</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The blur direction goes from the previous position to the current position.</p>\n<h3 id=\"blurring\">Blurring</h3>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  fragColor = texture(colorTexture, texCoord);</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the current fragment's color. This will be the first of the colors blurred together.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  direction.xy *= separation;</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a></span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Multiply the direction vector by the separation.</p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  vec2  forward  = texCoord;</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a>  vec2  backward = texCoord;</span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a></span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>For a more seamless blur, sample in the direction of the blur and in the opposite direction of the blur. For now, set the two vectors to the fragment's UV coordinate.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  <span class=\"dt\">float</span> count = <span class=\"fl\">1.0</span>;</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a></span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>count</code> is used to average all of the samples taken. It starts at one since you've already sampled the current fragment's color.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; size; ++i) {</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a>    forward  += direction;</span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a>    backward -= direction;</span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a></span>\n<span id=\"cb11-7\"><a href=\"#cb11-7\"></a>    fragColor +=</span>\n<span id=\"cb11-8\"><a href=\"#cb11-8\"></a>      texture</span>\n<span id=\"cb11-9\"><a href=\"#cb11-9\"></a>        ( colorTexture</span>\n<span id=\"cb11-10\"><a href=\"#cb11-10\"></a>        , forward</span>\n<span id=\"cb11-11\"><a href=\"#cb11-11\"></a>        );</span>\n<span id=\"cb11-12\"><a href=\"#cb11-12\"></a>    fragColor +=</span>\n<span id=\"cb11-13\"><a href=\"#cb11-13\"></a>      texture</span>\n<span id=\"cb11-14\"><a href=\"#cb11-14\"></a>        ( colorTexture</span>\n<span id=\"cb11-15\"><a href=\"#cb11-15\"></a>        , backward</span>\n<span id=\"cb11-16\"><a href=\"#cb11-16\"></a>        );</span>\n<span id=\"cb11-17\"><a href=\"#cb11-17\"></a></span>\n<span id=\"cb11-18\"><a href=\"#cb11-18\"></a>    count += <span class=\"fl\">2.0</span>;</span>\n<span id=\"cb11-19\"><a href=\"#cb11-19\"></a>  }</span>\n<span id=\"cb11-20\"><a href=\"#cb11-20\"></a></span>\n<span id=\"cb11-21\"><a href=\"#cb11-21\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the screen's colors both in the forward and backward direction of the blur. Be sure to add these samples together as you travel along.</p>\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>  fragColor /= count;</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a>}</span></code></pre></div>\n<p>The final fragment color is the average color of the samples taken.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/motion-blur.frag\" target=\"_blank\" rel=\"noopener noreferrer\">motion-blur.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"ssao.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"chromatic-aberration.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/normal-mapping.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Normal Mapping | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Normal Mapping | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Normal Mapping | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"cel-shading.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"deferred-rendering.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"normal-mapping\">Normal Mapping</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/M4eHo9I.gif\" alt=\"Normal Mapping\" title=\"Normal Mapping\">\n</p>\n\n<p>Normal mapping allows you to add surface details without adding any geometry. Typically, in a modeling program like Blender, you create a high poly and a low poly version of your mesh. You take the vertex normals from the high poly mesh and bake them into a texture. This texture is the normal map. Then inside the fragment shader, you replace the low poly mesh's vertex normals with the high poly mesh's normals you baked into the normal map. Now when you light your mesh, it will appear to have more polygons than it really has. This will keep your FPS high while at the same time retain most of the details from the high poly version.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/nSY9AW4.gif\" alt=\"From high to low poly with normal mapping.\" title=\"From high to low poly with normal mapping.\">\n</p>\n\n<p>Here you see the progression from the high poly model to the low poly model to the low poly model with the normal map applied.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/jvkRPE7.gif\" alt=\"Normal Map Illusion\" title=\"Normal Map Illusion\">\n</p>\n\n<p>Keep in mind though, normal mapping is only an illusion. After a certain angle, the surface will look flat again.</p>\n<h3 id=\"vertex\">Vertex</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform mat3 p3d_NormalMatrix;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>in vec3 p3d_Normal;</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a></span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a></span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a>in vec3 p3d_Binormal;</span>\n<span id=\"cb1-12\"><a href=\"#cb1-12\"></a>in vec3 p3d_Tangent;</span>\n<span id=\"cb1-13\"><a href=\"#cb1-13\"></a></span>\n<span id=\"cb1-14\"><a href=\"#cb1-14\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-15\"><a href=\"#cb1-15\"></a></span>\n<span id=\"cb1-16\"><a href=\"#cb1-16\"></a>  vertexNormal = normalize(p3d_NormalMatrix * p3d_Normal);</span>\n<span id=\"cb1-17\"><a href=\"#cb1-17\"></a>  binormal     = normalize(p3d_NormalMatrix * p3d_Binormal);</span>\n<span id=\"cb1-18\"><a href=\"#cb1-18\"></a>  tangent      = normalize(p3d_NormalMatrix * p3d_Tangent);</span>\n<span id=\"cb1-19\"><a href=\"#cb1-19\"></a></span>\n<span id=\"cb1-20\"><a href=\"#cb1-20\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Starting in the vertex shader, you'll need to output to the fragment shader the normal vector, binormal vector, and the tangent vector. These vectors are used, in the fragment shader, to transform the normal map normal from tangent space to view space.</p>\n<p><code>p3d_NormalMatrix</code> transforms the vertex normal, binormal, and tangent vectors to view space. Remember that in view space, all of the coordinates are relative to the camera's position.</p>\n<blockquote>\n[p3d_NormalMatrix] is the upper 3x3 of the inverse transpose of the ModelViewMatrix.\nIt is used to transform the normal vector into view-space coordinates.\n<br>\n<br>\n<footer>\n<a href=\"http://www.panda3d.org/manual/?title=List_of_GLSL_Shader_Inputs\">Source</a>\n</footer>\n</blockquote>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>in vec2 p3d_MultiTexCoord0;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>out vec2 normalCoord;</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a></span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a></span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>  normalCoord   = p3d_MultiTexCoord0;</span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a></span>\n<span id=\"cb2-13\"><a href=\"#cb2-13\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tLIA6Hu.gif\" alt=\"Normal Maps\" title=\"Normal Maps\">\n</p>\n\n<p>You'll also need to output, to the fragment shader, the UV coordinates for the normal map.</p>\n<h3 id=\"fragment\">Fragment</h3>\n<p>Recall that the vertex normal was used to calculate the lighting. However, the normal map provides us with different normals to use when calculating the lighting. In the fragment shader, you need to swap out the vertex normals for the normals found in the normal map.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform sampler2D p3d_Texture1;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>in vec2 normalCoord;</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a></span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a></span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a>  <span class=\"co\">/* Find */</span></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>  vec4 normalTex   = texture(p3d_Texture1, normalCoord);</span>\n<span id=\"cb3-13\"><a href=\"#cb3-13\"></a></span>\n<span id=\"cb3-14\"><a href=\"#cb3-14\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the normal map coordinates the vertex shader sent, pull out the normal from the normal map.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec3 normal;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>    <span class=\"co\">/* Unpack */</span></span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>    normal =</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>      normalize</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>        ( normalTex.rgb</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a>        * <span class=\"fl\">2.0</span></span>\n<span id=\"cb4-12\"><a href=\"#cb4-12\"></a>        - <span class=\"fl\">1.0</span></span>\n<span id=\"cb4-13\"><a href=\"#cb4-13\"></a>        );</span>\n<span id=\"cb4-14\"><a href=\"#cb4-14\"></a></span>\n<span id=\"cb4-15\"><a href=\"#cb4-15\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Earlier I showed how the normals are mapped to colors to create the normal map. Now this process needs to be reversed so you can get back the original normals that were baked into the map.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>(r, g, b) =</span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a>  ( r * <span class=\"dv\">2</span> - <span class=\"dv\">1</span></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  , g * <span class=\"dv\">2</span> - <span class=\"dv\">1</span></span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  , b * <span class=\"dv\">2</span> - <span class=\"dv\">1</span></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>  ) =</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>    (x, y, z)</span></code></pre></div>\n<p>Here's the process for unpacking the normals from the normal map.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>    <span class=\"co\">/* Transform */</span></span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>    normal =</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>      normalize</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>        ( mat3</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>            ( tangent</span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>            , binormal</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>            , vertexNormal</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a>            )</span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a>        * normal</span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\"></a>        );</span>\n<span id=\"cb6-13\"><a href=\"#cb6-13\"></a></span>\n<span id=\"cb6-14\"><a href=\"#cb6-14\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The normals you get back from the normal map are typically in tangent space. They could be in another space, however. For example, Blender allows you to bake the normals in tangent, object, world, or camera space.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EzHJPd4.gif\" alt=\"Replacing the vertex normals with the normal map normals.\" title=\"Replacing the vertex normals with the normal map normals.\">\n</p>\n\n<p>To take the normal map normal from tangent space to view pace, construct a three by three matrix using the tangent, binormal, and vertex normal vectors. Multiply the normal by this matrix and be sure to normalize it.</p>\n<p>At this point, you're done. The rest of the lighting calculations are the same.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"cel-shading.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"deferred-rendering.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/outlining.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Outlining | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Outlining | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Outlining | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"flow-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"depth-of-field.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"outlining\">Outlining</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EKaZRkR.gif\" alt=\"Outlined Scene\" title=\"Outlined Scene\">\n</p>\n\n<p>Outlining your scene's geometry can give your game a distinctive look, reminiscent of comic books and cartoons.</p>\n<h3 id=\"discontinuities\">Discontinuities</h3>\n<p>The process of outlining is the process of finding and labeling discontinuities or differences. Every time you find what you consider a significant difference, you mark it with your line color. As you go about labeling or coloring in the differences, outlines or edges will start to form.</p>\n<p>Where you choose to search for the discontinuities is up to you. It could be the diffuse colors in your scene, the normals of your models, the depth buffer, or some other scene related data.</p>\n<p>The demo uses the interpolated vertex positions to render the outlines. However, a less straightforward but more typical way is to use both the scene's normals and depth buffer values to construct the outlines.</p>\n<h3 id=\"vertex-positions\">Vertex Positions</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform sampler2D positionTexture;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like SSAO, you'll need the vertex positions in view space. Referrer back to <a href=\"ssao.html#vertex-positions\">SSAO</a> for details.</p>\n<h3 id=\"scene-colors\">Scene Colors</h3>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform sampler2D colorTexture;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>The demo darkens the colors of the scene where there's an outline. This tends to look nicer than a constant color since it provides some color variation to the edges.</p>\n<h3 id=\"parameters\">Parameters</h3>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  <span class=\"dt\">float</span> minSeparation = <span class=\"fl\">1.0</span>;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  <span class=\"dt\">float</span> maxSeparation = <span class=\"fl\">3.0</span>;</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"dt\">float</span> minDistance   = <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  <span class=\"dt\">float</span> maxDistance   = <span class=\"fl\">2.0</span>;</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  <span class=\"dt\">int</span>   size          = <span class=\"dv\">1</span>;</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  vec3 colorModifier  = vec3(<span class=\"fl\">0.324</span>, <span class=\"fl\">0.063</span>, <span class=\"fl\">0.099</span>);</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a></span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The min and max separation parameters control the thickness of the outline depending on the fragment's distance from the camera or depth. The min and max distance control the significance of any changes found. The <code>size</code> parameter controls the constant thickness of the line no matter the fragment's position. The outline color is based on <code>colorModifier</code> and the current fragment's color.</p>\n<h3 id=\"fragment-position\">Fragment Position</h3>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec2 texSize   = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec2 fragCoord = gl_FragCoord.xy;</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  vec2 texCoord  = fragCoord / texSize;</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>  vec4 position = texture(positionTexture, texCoord);</span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a></span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the position texture for the current fragment's position in the scene. Recall that the position texture is just a screen shaped quad making the UV coordinate the current fragment's screen coordinate divided by the dimensions of the screen.</p>\n<h3 id=\"fragment-depth\">Fragment Depth</h3>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  <span class=\"dt\">float</span> depth =</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>    clamp</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>      (   <span class=\"fl\">1.0</span></span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>        - ( (far - position.y)</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>          / (far - near)</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>        )</span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>      , <span class=\"fl\">0.0</span></span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a>      , <span class=\"fl\">1.0</span></span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\"></a>      );</span>\n<span id=\"cb5-12\"><a href=\"#cb5-12\"></a></span>\n<span id=\"cb5-13\"><a href=\"#cb5-13\"></a>  <span class=\"dt\">float</span> separation = mix(maxSeparation, minSeparation, depth);</span>\n<span id=\"cb5-14\"><a href=\"#cb5-14\"></a></span>\n<span id=\"cb5-15\"><a href=\"#cb5-15\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fragment's depth ranges from zero to one. When the fragment's view-space y coordinate matches the far clipping plane, the depth is one. When the fragment's view-space y coordinate matches the near clipping plane, the depth is zero. In other words, the depth ranges from zero at the near clipping plane all the way up to one at the far clipping plane.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  <span class=\"dt\">float</span> separation = mix(maxSeparation, minSeparation, depth);</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a></span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Converting the position to a depth value isn't necessary but it allows you to vary the thickness of the outline based on how far away the fragment is from the camera. Far away fragments get a thinner line while nearer fragments get a thicker outline. This tends to look nicer than a constant thickness since it gives depth to the outline.</p>\n<h3 id=\"finding-the-discontinuities\">Finding The Discontinuities</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/xAMRGhn.gif\" alt=\"Edge Finding\" title=\"Edge Finding\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  <span class=\"dt\">float</span> mx = <span class=\"fl\">0.0</span>;</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = -size; i &lt;= size; ++i) {</span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>    <span class=\"cf\">for</span> (<span class=\"dt\">int</span> j = -size; j &lt;= size; ++j) {</span>\n<span id=\"cb7-7\"><a href=\"#cb7-7\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb7-8\"><a href=\"#cb7-8\"></a>    }</span>\n<span id=\"cb7-9\"><a href=\"#cb7-9\"></a>  }</span>\n<span id=\"cb7-10\"><a href=\"#cb7-10\"></a></span>\n<span id=\"cb7-11\"><a href=\"#cb7-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now that you have the current fragment's position, loop through an i by j grid or window surrounding the current fragment.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>      texCoord =</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>          ( fragCoord</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>          + (vec2(i, j) * separation)</span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>          )</span>\n<span id=\"cb8-7\"><a href=\"#cb8-7\"></a>        / texSize;</span>\n<span id=\"cb8-8\"><a href=\"#cb8-8\"></a></span>\n<span id=\"cb8-9\"><a href=\"#cb8-9\"></a>      vec4 positionTemp =</span>\n<span id=\"cb8-10\"><a href=\"#cb8-10\"></a>        texture</span>\n<span id=\"cb8-11\"><a href=\"#cb8-11\"></a>          ( positionTexture</span>\n<span id=\"cb8-12\"><a href=\"#cb8-12\"></a>          , texCoord</span>\n<span id=\"cb8-13\"><a href=\"#cb8-13\"></a>          );</span>\n<span id=\"cb8-14\"><a href=\"#cb8-14\"></a></span>\n<span id=\"cb8-15\"><a href=\"#cb8-15\"></a>      mx = max(mx, abs(position.y - positionTemp.y));</span>\n<span id=\"cb8-16\"><a href=\"#cb8-16\"></a></span>\n<span id=\"cb8-17\"><a href=\"#cb8-17\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>With each iteration, find the biggest distance between this fragment's and the surrounding fragments' positions.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/idDZr62.png\" alt=\"smoothstep\" title=\"smoothstep\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  <span class=\"dt\">float</span> diff = smoothstep(minDistance, maxDistance, mx);</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a></span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the significance of any difference discovered using the <code>minDistance</code>, <code>maxDistance</code>, and <code>smoothstep</code>. <code>smoothstep</code> returns values from zero to one. The <code>minDistance</code> is the left-most edge. Any difference less than the minimum distance will be zero. The <code>maxDistance</code> is the right-most edge. Any difference greater than the maximum distance will be one. For distances between the edges, the difference will be between zero and one. These values are interpolated along a s-shaped curve.</p>\n<h3 id=\"line-color\">Line Color</h3>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  vec3 lineColor = texture(colorTexture, texCoord).rgb * colorModifier;</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a></span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The line color is the current fragment color either darkened or lightened.</p>\n<h3 id=\"fragment-color\">Fragment Color</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/yWNdPZe.gif\" alt=\"Outlines\" title=\"Outlines\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>  fragColor.rgb = vec4(lineColor, diff);</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a></span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fragment's RGB color is the <code>lineColor</code> and its alpha channel is <code>diff</code>.</p>\n<h3 id=\"sketchy\">Sketchy</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FRBkSm1.gif\" alt=\"Sketchy Outline\" title=\"Sketchy Outline\">\n</p>\n\n<p>For a sketchy outline, you can distort the UV coordinates used to sample the position vectors.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/qgZNtnN.png\" alt=\"Outline Noise\" title=\"Outline Noise\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>uniform sampler2D noiseTexture;</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a></span>\n<span id=\"cb12-5\"><a href=\"#cb12-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Start by creating a RGB noise texture. A good size is either 128 by 128 or 512 by 512. Be sure to blur it and make it tileable. This will produce a nice wavy, inky outline.</p>\n<div class=\"sourceCode\" id=\"cb13\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb13-2\"><a href=\"#cb13-2\"></a></span>\n<span id=\"cb13-3\"><a href=\"#cb13-3\"></a>  <span class=\"dt\">float</span> noiseScale = <span class=\"fl\">10.0</span>;</span>\n<span id=\"cb13-4\"><a href=\"#cb13-4\"></a></span>\n<span id=\"cb13-5\"><a href=\"#cb13-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>noiseScale</code> parameter controls how distorted the outline is. The bigger the <code>noiseScale</code>, the sketchier the line.</p>\n<div class=\"sourceCode\" id=\"cb14\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb14-1\"><a href=\"#cb14-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb14-2\"><a href=\"#cb14-2\"></a></span>\n<span id=\"cb14-3\"><a href=\"#cb14-3\"></a>  vec2 fragCoord = gl_FragCoord.xy;</span>\n<span id=\"cb14-4\"><a href=\"#cb14-4\"></a></span>\n<span id=\"cb14-5\"><a href=\"#cb14-5\"></a>  vec2 noise  = texture(noiseTexture, fragCoord / textureSize(noiseTexture, <span class=\"dv\">0</span>).xy).rb;</span>\n<span id=\"cb14-6\"><a href=\"#cb14-6\"></a>       noise  = noise * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span>;</span>\n<span id=\"cb14-7\"><a href=\"#cb14-7\"></a>       noise *= noiseScale;</span>\n<span id=\"cb14-8\"><a href=\"#cb14-8\"></a></span>\n<span id=\"cb14-9\"><a href=\"#cb14-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the noise texture using the current screen/fragment position and the size of the noise texture. Since you're distorting the UV coordinates used to sample the position vectors, you'll only need two of the three color channels. Map the two color channels from <code>[0, 1]</code> to <code>[-1, 1]</code>. Finally, scale the noise by the scale chosen earlier.</p>\n<div class=\"sourceCode\" id=\"cb15\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb15-1\"><a href=\"#cb15-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb15-2\"><a href=\"#cb15-2\"></a></span>\n<span id=\"cb15-3\"><a href=\"#cb15-3\"></a>  vec2 texSize  = textureSize(colorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb15-4\"><a href=\"#cb15-4\"></a>  vec2 texCoord = (fragCoord - noise) / texSize;</span>\n<span id=\"cb15-5\"><a href=\"#cb15-5\"></a></span>\n<span id=\"cb15-6\"><a href=\"#cb15-6\"></a>  vec4 position = texture(positionTexture, texCoord);</span>\n<span id=\"cb15-7\"><a href=\"#cb15-7\"></a></span>\n<span id=\"cb15-8\"><a href=\"#cb15-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>When sampling the current position, subtract the noise vector from the current fragment's coordinates.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/wk43ybP.png\" alt=\"Squiggly Outline\" title=\"Squiggly Outline\">\n</p>\n\n<p>You could instead add it to the current fragment's coordinates which will create more of a squiggly line that loosely follows the geometry.</p>\n<div class=\"sourceCode\" id=\"cb16\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb16-1\"><a href=\"#cb16-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb16-2\"><a href=\"#cb16-2\"></a></span>\n<span id=\"cb16-3\"><a href=\"#cb16-3\"></a>      texCoord =</span>\n<span id=\"cb16-4\"><a href=\"#cb16-4\"></a>          (vec2(i, j) * separation + fragCoord + noise)</span>\n<span id=\"cb16-5\"><a href=\"#cb16-5\"></a>        / texSize;</span>\n<span id=\"cb16-6\"><a href=\"#cb16-6\"></a></span>\n<span id=\"cb16-7\"><a href=\"#cb16-7\"></a>      <span class=\"co\">// ...</span></span>\n<span id=\"cb16-8\"><a href=\"#cb16-8\"></a></span>\n<span id=\"cb16-9\"><a href=\"#cb16-9\"></a>      vec4 positionTemp =</span>\n<span id=\"cb16-10\"><a href=\"#cb16-10\"></a>        texture</span>\n<span id=\"cb16-11\"><a href=\"#cb16-11\"></a>          ( positionTexture</span>\n<span id=\"cb16-12\"><a href=\"#cb16-12\"></a>          , texCoord</span>\n<span id=\"cb16-13\"><a href=\"#cb16-13\"></a>          );</span>\n<span id=\"cb16-14\"><a href=\"#cb16-14\"></a></span>\n<span id=\"cb16-15\"><a href=\"#cb16-15\"></a>      <span class=\"co\">// ...</span></span></code></pre></div>\n<p>When sampling the surrounding positions inside the loop, add the noise vector to the current fragment's coordinates. The rest of the calculations are the same.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/outline.frag\" target=\"_blank\" rel=\"noopener noreferrer\">outline.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"flow-mapping.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"depth-of-field.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/pixelization.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Pixelization | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Pixelization | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Pixelization | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"posterization.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"sharpen.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"pixelization\">Pixelization</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IbnyYZN.gif\" alt=\"Pixelization\" title=\"Pixelization\">\n</p>\n\n<p>Pixelizing your 3D game can give it a interesting look and possibly save you time by not having to create all of the pixel art by hand. Combine it with the posterization for a true retro look.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">int</span> pixelSize = <span class=\"dv\">5</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Feel free to adjust the pixel size. The bigger the pixel size, the blockier the image will be.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WF5MmM0.gif\" alt=\"Pixelization Process\" title=\"Pixelization Process\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> x = <span class=\"dt\">int</span>(gl_FragCoord.x) % pixelSize;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  <span class=\"dt\">float</span> y = <span class=\"dt\">int</span>(gl_FragCoord.y) % pixelSize;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  x = floor(pixelSize / <span class=\"fl\">2.0</span>) - x;</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  y = floor(pixelSize / <span class=\"fl\">2.0</span>) - y;</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a></span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a>  x = gl_FragCoord.x + x;</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a>  y = gl_FragCoord.y + y;</span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a></span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The technique works by mapping each fragment to the center of its closest, non-overlapping pixel-sized window. These windows are laid out in a grid over the input texture. The center-of-the-window fragments determine the color for the other fragments in their window.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>    fragColor = texture(colorTexture, vec2(x, y) / texSize);</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Once you have determined the correct fragment coordinate to use, pull its color from the input texture and assign that to the fragment color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/pixelize.frag\" target=\"_blank\" rel=\"noopener noreferrer\">pixelize.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"posterization.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"sharpen.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/posterization.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Posterization | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Posterization | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Posterization | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"depth-of-field.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"pixelization.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"posterization\">Posterization</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/hpP5G9z.gif\" alt=\"Posterization\" title=\"Posterization\">\n</p>\n\n<p>Posterization or color quantization is the process of reducing the number of unique colors in an image. You can use this shader to give your game a comic book or retro look. Combine it with <a href=\"outlining.html\">outlining</a> for a full-on cartoon art style.</p>\n<p>There are various different ways to implement posterization. This method works directly with the greyscale values and indirectly with the RGB values of the image. For each fragment, it maps the RGB color to a greyscale value. This greyscale value is then mapped to both its lower and upper level value. The closest level to the original greyscale value is then mapped back to an RGB value This new RGB value becomes the fragment color. I find this method produces nicer results than the more typical methods you'll find.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">float</span> levels = <span class=\"dv\">10</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>levels</code> parameter controls how many discrete bands or steps there are. This will break up the continuous values from zero to one into chunks. With four levels, <code>0.0</code> to <code>1.0</code> becomes <code>0.0</code>, <code>0.25</code>, <code>0.5</code>, <code>0.75</code>, and <code>1.0</code>.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  fragColor = texture(posterizeTexture, texCoord);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Sample the current fragment's color.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  <span class=\"dt\">float</span> greyscale = max(fragColor.r, max(fragColor.g, fragColor.b));</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Map the RGB values to a greyscale value. In this instance, the greyscale value is the maximum value of the R, G, and B values.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  <span class=\"dt\">float</span> lower     = floor(greyscale * levels) / levels;</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  <span class=\"dt\">float</span> lowerDiff = abs(greyscale - lower);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Map the greyscale value to its lower level and then calculate the difference between its lower level and itself. For example, if the greyscale value is <code>0.87</code> and there are four levels, its lower level is <code>0.75</code> and the difference is <code>0.12</code>.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  <span class=\"dt\">float</span> upper     = ceil(greyscale * levels) / levels;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>  <span class=\"dt\">float</span> upperDiff = abs(upper - greyscale);</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a></span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now calculate the upper level and the difference. Keeping with the example up above, the upper level is <code>1.0</code> and the difference is <code>0.13</code>.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  <span class=\"dt\">float</span> level      = lowerDiff &lt;= upperDiff ? lower : upper;</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>  <span class=\"dt\">float</span> adjustment = level / greyscale;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The closest level is used to calculate the adjustment. The adjustment is the ratio between the quantized and unquantized greyscale value. This adjustment is used to map the quantized greyscale value back to an RGB value.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  fragColor.rgb * adjustment;</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a></span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>After multiplying <code>rgb</code> by the adjustment, <code>max(r, max(g, b))</code> will now equal the quantized greyscale value. This maps the quantized greyscale value back to a red, green, and blue vector.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/posterize.frag\" target=\"_blank\" rel=\"noopener noreferrer\">posterize.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"depth-of-field.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"pixelization.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/reference-frames.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Reference Frames | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Reference Frames | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Reference Frames | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"running-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"glsl.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"reference-frames\">Reference Frames</h2>\n<p>Before you write any shaders, you should be familiar with the following frames of reference or coordinate systems. All of them boil down to what origin <code>(0, 0, 0)</code> are these coordinates currently relative to? Once you know that, you can then transform them, via some matrix, to some other vector space if need be. Typically, when the output of some shader looks wrong, it's because of some coordinate system mix up.</p>\n<h3 id=\"model\">Model</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/8xptajU.gif\" alt=\"Model Space\" title=\"Model Space\">\n</p>\n\n<p>The model or object coordinate system is relative to the origin of the model. This is typically set to the center of the model's geometry in a modeling program like Blender.</p>\n<h3 id=\"world\">World</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fHl4ohX.gif\" alt=\"World Space\" title=\"World Space\">\n</p>\n\n<p>The world space is relative to the origin of the scene/level/universe that you've created.</p>\n<h3 id=\"view\">View</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3b4SGGH.gif\" alt=\"View Space\" title=\"View Space\">\n</p>\n\n<p>The view or eye coordinate space is relative to the position of the active camera.</p>\n<h3 id=\"clip\">Clip</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iSEWS9Y.png\" alt=\"Clip Space\" title=\"Clip Space\">\n</p>\n\n<p>The clip space is relative to the center of the camera's film. All coordinates are now homogeneous, ranging from negative one to one <code>(-1, 1)</code>. X and y are parallel with the camera's film and the z coordinate is the depth.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MhgmOLv.gif\" alt=\"Frustum\" title=\"Frustum\">\n</p>\n\n<p>Any vertex not within the bounds of the camera's frustum or view volume is clipped or discarded. You can see this happening with the cube towards the back, clipped by the camera's far plane, and the cube off to the side.</p>\n<h3 id=\"screen\">Screen</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bHHrjOl.png\" alt=\"Screen Space\" title=\"Screen Space\">\n</p>\n\n<p>The screen space is (typically) relative to the lower left corner of the screen. X goes from zero to the screen width. Y goes from zero to the screen height.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"running-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"glsl.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/render-to-texture.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Render To Texture | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Render To Texture | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Render To Texture | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"glsl.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"texturing.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"render-to-texture\">Render To Texture</h2>\n<p>Instead of rendering/drawing/painting directly to the screen, the example code uses a technique called \"render to texture\". In order to render to a texture, you'll need to set up a framebuffer and bind a texture to it. Multiple textures can be bound to a single framebuffer.</p>\n<p>The textures bound to the framebuffer hold the vector(s) returned by the fragment shader. Typically these vectors are color vectors <code>(r, g, b, a)</code> but they could also be position or normal vectors <code>(x, y, z, w)</code>. For each bound texture, the fragment shader can output a different vector. For example you could output a vertex's position and normal in a single pass.</p>\n<p>Most of the example code dealing with Panda3D involves setting up <a href=\"https://www.panda3d.org/manual/?title=Render-to-Texture_and_Image_Postprocessing\">framebuffer textures</a>. To keep things straightforward, nearly all of the fragment shaders in the example code have only one output. However, you'll want to output as much as you can each render pass to keep your frames per second (FPS) high.</p>\n<p>There are two framebuffer texture setups found in the example code.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/t3iLKhx.gif\" alt=\"The first framebuffer texture setup.\" title=\"The first framebuffer texture setup.\">\n</p>\n\n<p>The first setup renders the mill scene into a framebuffer texture using a variety of vertex and fragment shaders. This setup will go through each of the mill scene's vertexes and corresponding fragments.</p>\n<p>In this setup, the example code performs the following.</p>\n<ul>\n<li>Stores geometry data (like vertex position or normal) for later use.</li>\n<li>Stores material data (like the diffuse color) for later use.</li>\n<li>UV maps the various textures (diffuse, normal, shadow, etc.).</li>\n<li>Calculates the ambient, diffuse, specular, and emission lighting.</li>\n</ul>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/o8H6cTy.png\" alt=\"The second framebuffer texture setup.\" title=\"The second framebuffer texture setup.\">\n</p>\n\n<p>The second setup is an orthographic camera pointed at a screen-shaped rectangle. This setup will go through just the four vertexes and their corresponding fragments.</p>\n<p>In this second setup, the example code performs the following.</p>\n<ul>\n<li>Manipulates the output of another framebuffer texture.</li>\n<li>Combines various framebuffer textures into one.</li>\n</ul>\n<p>I like to think of this second setup as using layers in GIMP, Krita, or Inkscape.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/L6Hwuxa.gif\" alt=\"Tabbing Through Framebuffer Textures\" title=\"Tabbing Through Framebuffer Textures\">\n</p>\n\n<p>In the example code, you can see the output of a particular framebuffer texture by using the <kbd>Tab</kbd> key or the <kbd>Shift</kbd>+<kbd>Tab</kbd> keys.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"glsl.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"texturing.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/rim-lighting.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Rim Lighting | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Rim Lighting | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Rim Lighting | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"fresnel-factor.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"cel-shading.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"rim-lighting\">Rim Lighting</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3erauzN.gif\" alt=\"Rim Lighting\" title=\"Rim Lighting\">\n</p>\n\n<p>Taking inspiration from the <a href=\"fresnel-factor.html\">fresnel factor</a>, rim lighting targets the rim or silhouette of an object. When combined with <a href=\"cel-shading.html\">cel shading</a> and <a href=\"outlining.html\">outlining</a>, it can really complete that cartoon look. You can also use it to highlight objects in the game, making it easier for players to navigate and accomplish tasks.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  vec3 eye = normalize(-vertexPosition.xyz);</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>As it was for the fresnel factor, you'll need the eye vector. If your vertex positions are in view space, the eye vector is the negation of the vertex position.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/mMsQFbE.gif\" alt=\"Rim Light\" title=\"Rim Light\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> rimLightIntensity = dot(eye, normal);</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>        rimLightIntensity = <span class=\"fl\">1.0</span> - rimLightIntensity;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>        rimLightIntensity = max(<span class=\"fl\">0.0</span>, rimLightIntensity);</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The Intensity of the rim light ranges from zero to one. When the eye and normal vector point in the same direction, the rim light intensity is zero. As the two vectors start to point in different directions, the rim light intensity increases until it eventually reaches one when the eye and normal become orthogonal or perpendicular to one another.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AAFI8p1.gif\" alt=\"Rim Light Power\" title=\"Rim Light Power\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  rimLightIntensity = pow(rimLightIntensity, rimLightPower);</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You can control the falloff of the rim light using the power function.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  rimLightIntensity = smoothstep(<span class=\"fl\">0.3</span>, <span class=\"fl\">0.4</span>, rimLightIntensity)</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>step</code> or <code>smoothstep</code> can also be used to control the falloff. This tends to look better when using <a href=\"cel-shading.html\">cel shading</a>. You'll learn more about these functions in later sections.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  vec4 rimLight   = rimLightIntensity * diffuse;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>       rimLight.a = diffuse.a;</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a></span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>What color you use for the rim light is up to you. The demo code multiplies the diffuse light by the <code>rimLightIntensity</code>. This will highlight the silhouette without overexposing it and without lighting any shadowed fragments.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  vec4 outputColor     = vec4(<span class=\"fl\">0.0</span>);</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>       outputColor.a   = diffuseColor.a;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>       outputColor.rgb =</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>           ambient.rgb</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>        +  diffuse.rgb</span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>        + specular.rgb</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>        + rimLight.rgb</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a>        + emission.rgb;</span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a></span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>After you've calculated the rim light, add it to the ambient, diffuse, specular, and emission lights.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2020 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"fresnel-factor.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"cel-shading.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/running-the-demo.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Running The Demo | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Running The Demo | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Running The Demo | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"building-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"reference-frames.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"running-the-demo\">Running The Demo</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/y5XcReP.gif\" alt=\"Running The Demo\" title=\"Running The Demo\">\n</p>\n\n<p>After you've built the example code, you can now run the executable or demo.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"ex\">./3d-game-shaders-for-beginners</span></span></code></pre></div>\n<p>Here's how you run it on Linux or Mac.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode bash\"><code class=\"sourceCode bash\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"ex\">3d-game-shaders-for-beginners.exe</span></span></code></pre></div>\n<p>Here's how you run it on Windows.</p>\n<h3 id=\"demo-controls\">Demo Controls</h3>\n<p>The demo comes with both keyboard and mouse controls to move the camera around, toggle on and off the different effects, adjust the fog, and view the various different framebuffer textures.</p>\n<h4 id=\"mouse\">Mouse</h4>\n<p>You can rotate the scene around by holding down the <kbd>Left Mouse</kbd> button and dragging. Hold down the <kbd>Right Mouse</kbd> button and drag to move up, down, left, and/or right. To zoom in, roll the <kbd>Mouse Wheel</kbd> forward. To zoom out, roll the <kbd>Mouse Wheel</kbd> backward.</p>\n<p>You can also change the focus point using the mouse. To change the focus point, click anywhere on the scene using the <kbd>Middle Mouse</kbd> button.</p>\n<h4 id=\"keyboard\">Keyboard</h4>\n<ul>\n<li><kbd>w</kbd> to rotate the scene down.</li>\n<li><kbd>a</kbd> to rotate the scene clockwise.</li>\n<li><kbd>s</kbd> to rotate the scene up.</li>\n<li><kbd>d</kbd> to rotate the scene counterclockwise.</li>\n<li><kbd>z</kbd> to zoom in to the scene.</li>\n<li><kbd>x</kbd> to zoom out of the scene.</li>\n<li><kbd>⬅</kbd> to move left.</li>\n<li><kbd>➡</kbd> to move right.</li>\n<li><kbd>⬆</kbd> to move up.</li>\n<li><kbd>⬇</kbd> to move down.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>1</kbd> to show midday.</li>\n<li><kbd>2</kbd> to show midnight.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>Delete</kbd> to toggle the sound.</li>\n<li><kbd>3</kbd> to toggle fresnel.</li>\n<li><kbd>4</kbd> to toggle rim lighting.</li>\n<li><kbd>5</kbd> to toggle particles.</li>\n<li><kbd>6</kbd> to toggle motion blur.</li>\n<li><kbd>7</kbd> to toggle Kuwahara filtering.</li>\n<li><kbd>8</kbd> to toggle cel shading.</li>\n<li><kbd>9</kbd> to toggle lookup table processing.</li>\n<li><kbd>0</kbd> to toggle between Phong and Blinn-Phong.</li>\n<li><kbd>y</kbd> to toggle SSAO.</li>\n<li><kbd>u</kbd> to toggle outlining.</li>\n<li><kbd>i</kbd> to toggle bloom.</li>\n<li><kbd>o</kbd> to toggle normal mapping.</li>\n<li><kbd>p</kbd> to toggle fog.</li>\n<li><kbd>h</kbd> to toggle depth of field.</li>\n<li><kbd>j</kbd> to toggle posterization.</li>\n<li><kbd>k</kbd> to toggle pixelization.</li>\n<li><kbd>l</kbd> to toggle sharpen.</li>\n<li><kbd>n</kbd> to toggle film grain.</li>\n<li><kbd>m</kbd> to toggle screen space reflection.</li>\n<li><kbd>,</kbd> to toggle screen space refraction.</li>\n<li><kbd>.</kbd> to toggle flow mapping.</li>\n<li><kbd>/</kbd> to toggle the sun animation.</li>\n<li><kbd>\\</kbd> to toggle chromatic aberration.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>r</kbd> to reset the scene.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>[</kbd> to decrease the fog near distance.</li>\n<li><kbd>Shift</kbd>+<kbd>[</kbd> to increase the fog near distance.</li>\n<li><kbd>]</kbd> to increase the fog far distance.</li>\n<li><kbd>Shift</kbd>+<kbd>]</kbd> to decrease the fog far distance.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>Shift</kbd>+<kbd>-</kbd> to decrease the amount of foam.</li>\n<li><kbd>-</kbd> to increase the amount of foam.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>Shift</kbd>+<kbd>=</kbd> to decrease the relative index of refraction.</li>\n<li><kbd>=</kbd> to increase the relative index of refraction.</li>\n</ul>\n<p></p>\n\n<ul>\n<li><kbd>Tab</kbd> to move forward through the framebuffer textures.</li>\n<li><kbd>Shift</kbd>+<kbd>Tab</kbd> to move backward through the framebuffer textures.</li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"building-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"reference-frames.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/screen-space-reflection.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Screen Space Reflection | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Screen Space Reflection | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Screen Space Reflection | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"chromatic-aberration.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"screen-space-refraction.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"screen-space-reflection-ssr\">Screen Space Reflection (SSR)</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AYG4tvg.gif\" alt=\"Screen Space Reflections\" title=\"Screen Space Reflections\">\n</p>\n\n<p>Adding reflections can really ground your scene. Wet and shiny objects spring to life as nothing makes something look wet or shiny quite like reflections. With reflections, you can really sell the illusion of water and metallic objects.</p>\n<p>In the <a href=\"lighting.html\">lighting</a> section, you simulated the reflected, mirror-like image of the light source. This was the process of rendering the specular reflection. Recall that the specular light was computed using the reflected light direction. Similarly, using screen space reflection or SSR, you can simulate the reflection of other objects in the scene instead of just the light source. Instead of the light ray coming from the source and bouncing off into the camera, the light ray comes from some object in the scene and bounces off into the camera.</p>\n<p>SSR works by reflecting the screen image onto itself using only itself. Compare this to cube mapping which uses six screens or textures. In cube mapping, you reflect a ray from some point in your scene to some point on the inside of a cube surrounding your scene. In SSR, you reflect a ray from some point on your screen to some other point on your screen. By reflecting your screen onto itself, you can create the illusion of reflection. This illusion holds for the most part but SSR does fail in some cases as you'll see.</p>\n<h3 id=\"ray-marching\">Ray Marching</h3>\n<p>Screen space reflection uses a technique known as ray marching to determine the reflection for each fragment. Ray marching is the process of iteratively extending or contracting the length or magnitude of some vector in order to probe or sample some space for information. The ray in screen space reflection is the position vector reflected about the normal.</p>\n<p>Intuitively, a light ray hits some point in the scene, bounces off, travels in the opposite direction of the reflected position vector, bounces off the current fragment, travels in the opposite direction of the position vector, and hits the camera lens allowing you to see the color of some point in the scene reflected in the current fragment. SSR is the process of tracing the light ray's path in reverse. It tries to find the reflected point the light ray bounced off of and hit the current fragment. With each iteration, the algorithm samples the scene's positions or depths, along the reflection ray, asking each time if the ray intersected with the scene's geometry. If there is an intersection, that position in the scene is a potential candidate for being reflected by the current fragment.</p>\n<p>Ideally there would be some analytical method for determining the first intersection point exactly. This first intersection point is the only valid point to reflect in the current fragment. Instead, this method is more like a game of battleship. You can't see the intersections (if there are any) so you start at the base of the reflection ray and call out coordinates as you travel in the direction of the reflection. With each call, you get back an answer of whether or not you hit something. If you do hit something, you try points around that area hoping to find the exact point of intersection.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/wnAC7NI.gif\" alt=\"SSR Ray Marching\" title=\"SSR Ray Marching\">\n</p>\n\n<p>Here you see ray marching being used to calculate each fragment's reflected point. The vertex normal is the bright green arrow, the position vector is the bright blue arrow, and the bright red vector is the reflection ray marching through view space.</p>\n<h3 id=\"vertex-positions\">Vertex Positions</h3>\n<p>Like SSAO, you'll need the vertex positions in view space. Referrer back to <a href=\"ssao.html#vertex-positions\">SSAO</a> for details.</p>\n<h3 id=\"vertex-normals\">Vertex Normals</h3>\n<p>To compute the reflections, you'll need the vertex normals in view space. Referrer back to <a href=\"ssao.html#vertex-normals\">SSAO</a> for details.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/1cE2vBY.gif\" alt=\"SSR using normal maps.\" title=\"SSR using normal maps.\">\n</p>\n\n<p>Here you see SSR using the normal mapped normals instead of the vertex normals. Notice how the reflection follows the ripples in the water versus the more mirror like reflection shown earlier.</p>\n<p>To use the normal maps instead, you'll need to transform the normal mapped normals from tangent space to view space just like you did in the lighting calculations. You can see this being done in <a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a>.</p>\n<h3 id=\"position-transformations\">Position Transformations</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Qnsvkc0.gif\" alt=\"Position Transformations\" title=\"Position Transformations\">\n</p>\n\n<p>Just like <a href=\"ssao.html\">SSAO</a>, SSR goes back and forth between the screen and view space. You'll need the camera lens' projection matrix to transform points in view space to clip space. From clip space, you'll have to transform the points again to UV space. Once in UV space, you can sample a vertex/fragment position from the scene which will be the closest position in the scene to your sample. This is the <em>screen space</em> part in <em>screen space reflection</em> since the \"screen\" is a texture UV mapped over a screen shaped rectangle.</p>\n<h3 id=\"reflected-uv-coordinates\">Reflected UV Coordinates</h3>\n<p>There are a few ways you can implement SSR. The example code starts the reflection process by computing a reflected UV coordinate for each screen fragment. You could skip this part and go straight to computing the reflected color instead, using the final rendering of the scene.</p>\n<p>Recall that UV coordinates range from zero to one for both U and V. The screen is just a 2D texture UV mapped over a screen-sized rectangle. Knowing this, the example code doesn't actually need the final rendering of the scene to compute the reflections. It can instead calculate what UV coordinate each screen pixel will eventually use. These calculated UV coordinates can be saved to a framebuffer texture and used later when the scene has been rendered.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/pkQcGkD.gif\" alt=\"Reflected UVs\" title=\"Reflected UVs\">\n</p>\n\n<p>Here you see the reflected UV coordinates. Without even rendering the scene yet, you can get a good feel for what the reflections will look like.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">//...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform mat4 lensProjection;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>uniform sampler2D positionTexture;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>uniform sampler2D normalTexture;</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a></span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a><span class=\"co\">//...</span></span></code></pre></div>\n<p>You'll need the camera lens' projection matrix as well as the interpolated vertex positions and normals in view space.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> maxDistance = <span class=\"dv\">15</span>;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  <span class=\"dt\">float</span> resolution  = <span class=\"fl\">0.3</span>;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>  <span class=\"dt\">int</span>   steps       = <span class=\"dv\">10</span>;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"dt\">float</span> thickness   = <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a></span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like the other effects, SSR has a few parameters you can adjust. Depending on the complexity of the scene, it may take you awhile to find the right settings. Getting screen space reflections to look just right tends to be difficult when reflecting complex geometry.</p>\n<p>The <code>maxDistance</code> parameter controls how far a fragment can reflect. In other words, it controls the maximum length or magnitude of the reflection ray.</p>\n<p>The <code>resolution</code> parameter controls how many fragments are skipped while traveling or marching the reflection ray during the first pass. This first pass is to find a point along the ray's direction where the ray enters or goes behind some geometry in the scene. Think of this first pass as the rough pass. Note that the <code>resolution</code> ranges from zero to one. Zero will result in no reflections while one will travel fragment-by-fragment along the ray's direction. A <code>resolution</code> of one can slow down your FPS considerably especially with a large <code>maxDistance</code>.</p>\n<p>The <code>steps</code> parameter controls how many iterations occur during the second pass. This second pass is to find the exact point along the reflection ray's direction where the ray immediately hits or intersects with some geometry in the scene. Think of this second pass as the refinement pass.</p>\n<p>The <code>thickness</code> controls the cutoff between what counts as a possible reflection hit and what does not. Ideally, you'd like to have the ray immediately stop at some camera-captured position or depth in the scene. This would be the exact point where the light ray bounced off, hit your current fragment, and then bounced off into the camera. Unfortunately the calculations are not always that precise so <code>thickness</code> provides some wiggle room or tolerance. You'll want the thickness to be as small as possible—just a short distance beyond a sampled position or depth.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/W163Mfw.png\" alt=\"Larger Thickness\" title=\"Larger Thickness\">\n</p>\n\n<p>You'll find that as the thickness gets larger, the reflections tend to smear in places.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/T6uH4Yu.png\" alt=\"Smaller Thickness\" title=\"Smaller Thickness\">\n</p>\n\n<p>Going in the other direction, as the thickness gets smaller, the reflections become noisy with tiny little holes and narrow gaps.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  vec2 texSize  = textureSize(positionTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  vec4 positionFrom     = texture(positionTexture, texCoord);</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  vec3 unitPositionFrom = normalize(positionFrom.xyz);</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  vec3 normal           = normalize(texture(normalTexture, texCoord).xyz);</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  vec3 pivot            = normalize(reflect(unitPositionFrom, normal));</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a></span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Gather the current fragment's position, normal, and reflection about the normal. <code>positionFrom</code> is a vector from the camera position to the current fragment position. <code>normal</code> is a vector pointing in the direction of the interpolated vertex normal for the current fragment. <code>pivot</code> is the reflection ray or vector pointing in the reflected direction of the <code>positionFrom</code> vector. It currently has a length or magnitude of one.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec4 startView = vec4(positionFrom.xyz + (pivot *           <span class=\"dv\">0</span>), <span class=\"dv\">1</span>);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec4 endView   = vec4(positionFrom.xyz + (pivot * maxDistance), <span class=\"dv\">1</span>);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the start and end point of the reflection ray in view space.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>  vec4 startFrag      = startView;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>       <span class=\"co\">// Project to screen space.</span></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>       startFrag      = lensProjection * startFrag;</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>       <span class=\"co\">// Perform the perspective divide.</span></span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>       startFrag.xyz /= startFrag.w;</span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>       <span class=\"co\">// Convert the screen-space XY coordinates to UV coordinates.</span></span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>       startFrag.xy   = startFrag.xy * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb5-10\"><a href=\"#cb5-10\"></a>       <span class=\"co\">// Convert the UV coordinates to fragment/pixel coordnates.</span></span>\n<span id=\"cb5-11\"><a href=\"#cb5-11\"></a>       startFrag.xy  *= texSize;</span>\n<span id=\"cb5-12\"><a href=\"#cb5-12\"></a></span>\n<span id=\"cb5-13\"><a href=\"#cb5-13\"></a>  vec4 endFrag      = endView;</span>\n<span id=\"cb5-14\"><a href=\"#cb5-14\"></a>       endFrag      = lensProjection * endFrag;</span>\n<span id=\"cb5-15\"><a href=\"#cb5-15\"></a>       endFrag.xyz /= endFrag.w;</span>\n<span id=\"cb5-16\"><a href=\"#cb5-16\"></a>       endFrag.xy   = endFrag.xy * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb5-17\"><a href=\"#cb5-17\"></a>       endFrag.xy  *= texSize;</span>\n<span id=\"cb5-18\"><a href=\"#cb5-18\"></a></span>\n<span id=\"cb5-19\"><a href=\"#cb5-19\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Project or transform these start and end points from view space to screen space. These points are now fragment positions which correspond to pixel positions on the screen. Now that you know where the ray starts and ends on the screen, you can travel or march along its direction in screen space. Think of the ray as a line drawn on the screen. You'll travel along this line using it to sample the fragment positions stored in the position framebuffer texture.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MpBR225.png\" alt=\"Screen space versus view space.\" title=\"Screen space versus view space.\">\n</p>\n\n<p>Note that you could march the ray through view space but this may under or over sample scene positions found in the position framebuffer texture. Recall that the position framebuffer texture is the size and shape of the screen. Every screen fragment or pixel corresponds to some position captured by the camera. A reflection ray may travel a long distance in view space, but in screen space, it may only travel through a few pixels. You can only sample the screen's pixels for positions so it is inefficient to potentially sample the same pixels over and over again while marching in view space. By marching in screen space, you'll more efficiently sample the fragments or pixels the ray actually occupies or covers.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>  vec2 frag  = startFrag.xy;</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>       uv.xy = frag / texSize;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a></span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The first pass will begin at the starting fragment position of the reflection ray. Convert the fragment position to a UV coordinate by dividing the fragment's coordinates by the position texture's dimensions.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  <span class=\"dt\">float</span> deltaX    = endFrag.x - startFrag.x;</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>  <span class=\"dt\">float</span> deltaY    = endFrag.y - startFrag.y;</span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a></span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the delta or difference between the X and Y coordinates of the end and start fragments. This will be how many pixels the ray line occupies in the X and Y dimension of the screen.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Um4dzgL.png\" alt=\"The reflection ray in screen space.\" title=\"The reflection ray in screen space.\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  <span class=\"dt\">float</span> useX      = abs(deltaX) &gt;= abs(deltaY) ? <span class=\"dv\">1</span> : <span class=\"dv\">0</span>;</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>  <span class=\"dt\">float</span> delta     = mix(abs(deltaY), abs(deltaX), useX) * clamp(resolution, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a></span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To handle all of the various different ways (vertical, horizontal, diagonal, etc.) the line can be oriented, you'll need to keep track of and use the larger difference. The larger difference will help you determine how much to travel in the X and Y direction each iteration, how many iterations are needed to travel the entire line, and what percentage of the line does the current position represent.</p>\n<p><code>useX</code> is either one or zero. It is used to pick the X or Y dimension depending on which delta is bigger. <code>delta</code> is the larger delta of the two X and Y deltas. It is used to determine how much to march in either dimension each iteration and how many iterations to take during the first pass.</p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  vec2  increment = vec2(deltaX, deltaY) / max(delta, <span class=\"fl\">0.001</span>);</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a></span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate how much to increment the X and Y position by using the larger of the two deltas. If the two deltas are the same, each will increment by one each iteration. If one delta is larger than the other, the larger delta will increment by one while the smaller one will increment by less than one. This assumes the <code>resolution</code> is one. If the resolution is less than one, the algorithm will skip over fragments.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>startFrag  = ( <span class=\"dv\">1</span>,  <span class=\"dv\">4</span>)</span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a>endFrag    = (<span class=\"dv\">10</span>, <span class=\"dv\">14</span>)</span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a></span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a>deltaX     = (<span class=\"dv\">10</span> - <span class=\"dv\">1</span>) = <span class=\"dv\">9</span></span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>deltaY     = (<span class=\"dv\">14</span> - <span class=\"dv\">4</span>) = <span class=\"dv\">10</span></span>\n<span id=\"cb10-6\"><a href=\"#cb10-6\"></a></span>\n<span id=\"cb10-7\"><a href=\"#cb10-7\"></a>resolution = <span class=\"fl\">0.5</span></span>\n<span id=\"cb10-8\"><a href=\"#cb10-8\"></a></span>\n<span id=\"cb10-9\"><a href=\"#cb10-9\"></a>delta      = <span class=\"dv\">10</span> * <span class=\"fl\">0.5</span> = <span class=\"dv\">5</span></span>\n<span id=\"cb10-10\"><a href=\"#cb10-10\"></a></span>\n<span id=\"cb10-11\"><a href=\"#cb10-11\"></a>increment  = (deltaX, deltaY) / delta</span>\n<span id=\"cb10-12\"><a href=\"#cb10-12\"></a>           = (     <span class=\"dv\">9</span>,     <span class=\"dv\">10</span>) / <span class=\"dv\">5</span></span>\n<span id=\"cb10-13\"><a href=\"#cb10-13\"></a>           = ( <span class=\"dv\">9</span> / <span class=\"dv\">5</span>,      <span class=\"dv\">2</span>)</span></code></pre></div>\n<p>For example, say the <code>resolution</code> is 0.5. The larger dimension will increment by two fragments instead of one.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>  <span class=\"dt\">float</span> search0 = <span class=\"dv\">0</span>;</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a>  <span class=\"dt\">float</span> search1 = <span class=\"dv\">0</span>;</span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a></span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To move from the start fragment to the end fragment, the algorithm uses linear interpolation.</p>\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>current position x = (start x) * (<span class=\"dv\">1</span> - search1) + (end x) * search1;</span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a>current position y = (start y) * (<span class=\"dv\">1</span> - search1) + (end y) * search1;</span></code></pre></div>\n<p><code>search1</code> ranges from zero to one. When <code>search1</code> is zero, the current position is the start fragment. When <code>search1</code> is one, the current position is the end fragment. For any other value, the current position is somewhere between the start and end fragment.</p>\n<p><code>search0</code> is used to remember the last position on the line where the ray missed or didn't intersect with any geometry. The algorithm will later use <code>search0</code> in the second pass to help refine the point at which the ray touches the scene's geometry.</p>\n<div class=\"sourceCode\" id=\"cb13\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb13-2\"><a href=\"#cb13-2\"></a></span>\n<span id=\"cb13-3\"><a href=\"#cb13-3\"></a>  <span class=\"dt\">int</span> hit0 = <span class=\"dv\">0</span>;</span>\n<span id=\"cb13-4\"><a href=\"#cb13-4\"></a>  <span class=\"dt\">int</span> hit1 = <span class=\"dv\">0</span>;</span>\n<span id=\"cb13-5\"><a href=\"#cb13-5\"></a></span>\n<span id=\"cb13-6\"><a href=\"#cb13-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>hit0</code> indicates there was an intersection during the first pass. <code>hit1</code> indicates there was an intersection during the second pass.</p>\n<div class=\"sourceCode\" id=\"cb14\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb14-1\"><a href=\"#cb14-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb14-2\"><a href=\"#cb14-2\"></a></span>\n<span id=\"cb14-3\"><a href=\"#cb14-3\"></a>  <span class=\"dt\">float</span> viewDistance = startView.y;</span>\n<span id=\"cb14-4\"><a href=\"#cb14-4\"></a>  <span class=\"dt\">float</span> depth        = thickness;</span>\n<span id=\"cb14-5\"><a href=\"#cb14-5\"></a></span>\n<span id=\"cb14-6\"><a href=\"#cb14-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>viewDistance</code> value is how far away from the camera the current point on the ray is. Recall that for Panda3D, the Y dimension goes in and out of the screen in view space. For other systems, the Z dimension goes in and out of the screen in view space. In any case, <code>viewDistance</code> is how far away from the camera the ray currently is. Note that if you use the depth buffer, instead of the vertex positions in view space, the <code>viewDistance</code> would be the Z depth.</p>\n<p>Make sure not to confuse the <code>viewDistance</code> value with the Y dimension of the line being traveled across the screen. The <code>viewDistance</code> goes from the camera into scene while the Y dimension of the line travels up or down the screen.</p>\n<p>The <code>depth</code> is the view distance difference between the current ray point and scene position. It tells you how far behind or in front of the scene the ray currently is. Remember that the scene positions are the interpolated vertex positions stored in the position framebuffer texture.</p>\n<div class=\"sourceCode\" id=\"cb15\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb15-1\"><a href=\"#cb15-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb15-2\"><a href=\"#cb15-2\"></a></span>\n<span id=\"cb15-3\"><a href=\"#cb15-3\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; <span class=\"dt\">int</span>(delta); ++i) {</span>\n<span id=\"cb15-4\"><a href=\"#cb15-4\"></a></span>\n<span id=\"cb15-5\"><a href=\"#cb15-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You can now begin the first pass. The first pass runs while <code>i</code> is less than the <code>delta</code> value. When <code>i</code> reaches <code>delta</code>, the algorithm has traveled the entire length of the line. Remember that <code>delta</code> is the larger of the two X and Y deltas.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Qnsvkc0.gif\" alt=\"Screen Space Transformations\" title=\"Screen Space Transformations\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb16\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb16-1\"><a href=\"#cb16-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb16-2\"><a href=\"#cb16-2\"></a></span>\n<span id=\"cb16-3\"><a href=\"#cb16-3\"></a>    frag      += increment;</span>\n<span id=\"cb16-4\"><a href=\"#cb16-4\"></a>    uv.xy      = frag / texSize;</span>\n<span id=\"cb16-5\"><a href=\"#cb16-5\"></a>    positionTo = texture(positionTexture, uv.xy);</span>\n<span id=\"cb16-6\"><a href=\"#cb16-6\"></a></span>\n<span id=\"cb16-7\"><a href=\"#cb16-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Advance the current fragment position closer to the end fragment. Use this new fragment position to look up a scene position stored in the position framebuffer texture.</p>\n<div class=\"sourceCode\" id=\"cb17\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb17-1\"><a href=\"#cb17-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb17-2\"><a href=\"#cb17-2\"></a></span>\n<span id=\"cb17-3\"><a href=\"#cb17-3\"></a>    search1 =</span>\n<span id=\"cb17-4\"><a href=\"#cb17-4\"></a>      mix</span>\n<span id=\"cb17-5\"><a href=\"#cb17-5\"></a>        ( (frag.y - startFrag.y) / deltaY</span>\n<span id=\"cb17-6\"><a href=\"#cb17-6\"></a>        , (frag.x - startFrag.x) / deltaX</span>\n<span id=\"cb17-7\"><a href=\"#cb17-7\"></a>        , useX</span>\n<span id=\"cb17-8\"><a href=\"#cb17-8\"></a>        );</span>\n<span id=\"cb17-9\"><a href=\"#cb17-9\"></a></span>\n<span id=\"cb17-10\"><a href=\"#cb17-10\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the percentage or portion of the line the current fragment represents. If <code>useX</code> is zero, use the Y dimension of the line. If <code>useX</code> is one, use the X dimension of the line.</p>\n<p>When <code>frag</code> equals <code>startFrag</code>, <code>search1</code> equals zero since <code>frag - startFrag</code> is zero. When <code>frag</code> equals <code>endFrag</code>, <code>search1</code> is one since <code>frag - startFrag</code> equals <code>delta</code>.</p>\n<p><code>search1</code> is the percentage or portion of the line the current position represents. You'll need this to interpolate between the ray's view-space start and end distances from the camera.</p>\n<div class=\"sourceCode\" id=\"cb18\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb18-1\"><a href=\"#cb18-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb18-2\"><a href=\"#cb18-2\"></a></span>\n<span id=\"cb18-3\"><a href=\"#cb18-3\"></a>    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);</span>\n<span id=\"cb18-4\"><a href=\"#cb18-4\"></a></span>\n<span id=\"cb18-5\"><a href=\"#cb18-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using <code>search1</code>, interpolate the view distance (distance from the camera in view space) for the current position you're at on the reflection ray.</p>\n<div class=\"sourceCode\" id=\"cb19\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb19-1\"><a href=\"#cb19-1\"></a><span class=\"co\">// Incorrect.</span></span>\n<span id=\"cb19-2\"><a href=\"#cb19-2\"></a>viewDistance = mix(startView.y, endView.y, search1);</span>\n<span id=\"cb19-3\"><a href=\"#cb19-3\"></a></span>\n<span id=\"cb19-4\"><a href=\"#cb19-4\"></a><span class=\"co\">// Correct.</span></span>\n<span id=\"cb19-5\"><a href=\"#cb19-5\"></a>viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);</span></code></pre></div>\n<p>You may be tempted to just interpolate between the view distances of the start and end view-space positions but this will give you the wrong view distance for the current position on the reflection ray. Instead, you'll need to perform <a href=\"https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf\">perspective-correct interpolation</a> which you see here.</p>\n<div class=\"sourceCode\" id=\"cb20\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb20-1\"><a href=\"#cb20-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb20-2\"><a href=\"#cb20-2\"></a></span>\n<span id=\"cb20-3\"><a href=\"#cb20-3\"></a>    depth        = viewDistance - positionTo.y;</span>\n<span id=\"cb20-4\"><a href=\"#cb20-4\"></a></span>\n<span id=\"cb20-5\"><a href=\"#cb20-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the difference between the ray's view distance at this point and the sampled view distance of the scene at this point.</p>\n<div class=\"sourceCode\" id=\"cb21\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb21-1\"><a href=\"#cb21-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb21-2\"><a href=\"#cb21-2\"></a></span>\n<span id=\"cb21-3\"><a href=\"#cb21-3\"></a>    <span class=\"cf\">if</span> (depth &gt; <span class=\"dv\">0</span> &amp;&amp; depth &lt; thickness) {</span>\n<span id=\"cb21-4\"><a href=\"#cb21-4\"></a>      hit0 = <span class=\"dv\">1</span>;</span>\n<span id=\"cb21-5\"><a href=\"#cb21-5\"></a>      <span class=\"cf\">break</span>;</span>\n<span id=\"cb21-6\"><a href=\"#cb21-6\"></a>    } <span class=\"cf\">else</span> {</span>\n<span id=\"cb21-7\"><a href=\"#cb21-7\"></a>      search0 = search1;</span>\n<span id=\"cb21-8\"><a href=\"#cb21-8\"></a>    }</span>\n<span id=\"cb21-9\"><a href=\"#cb21-9\"></a></span>\n<span id=\"cb21-10\"><a href=\"#cb21-10\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the difference is between zero and the thickness, this is a hit. Set <code>hit0</code> to one and exit the first pass. If the difference is not between zero and the thickness, this is a miss. Set <code>search0</code> to equal <code>search1</code> to remember this position as the last known miss. Continue marching the ray towards the end fragment.</p>\n<div class=\"sourceCode\" id=\"cb22\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb22-1\"><a href=\"#cb22-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb22-2\"><a href=\"#cb22-2\"></a></span>\n<span id=\"cb22-3\"><a href=\"#cb22-3\"></a>  search1 = search0 + ((search1 - search0) / <span class=\"dv\">2</span>);</span>\n<span id=\"cb22-4\"><a href=\"#cb22-4\"></a></span>\n<span id=\"cb22-5\"><a href=\"#cb22-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>At this point you have finished the first pass. Set the <code>search1</code> position to be halfway between the position of the last miss and the position of the last hit.</p>\n<div class=\"sourceCode\" id=\"cb23\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb23-1\"><a href=\"#cb23-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb23-2\"><a href=\"#cb23-2\"></a></span>\n<span id=\"cb23-3\"><a href=\"#cb23-3\"></a>  steps *= hit0;</span>\n<span id=\"cb23-4\"><a href=\"#cb23-4\"></a></span>\n<span id=\"cb23-5\"><a href=\"#cb23-5\"></a>  <span class=\"cf\">for</span> (i = <span class=\"dv\">0</span>; i &lt; steps; ++i) {</span>\n<span id=\"cb23-6\"><a href=\"#cb23-6\"></a></span>\n<span id=\"cb23-7\"><a href=\"#cb23-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You can now begin the second pass. If the reflection ray didn't hit anything in the first pass, skip the second pass.</p>\n<div class=\"sourceCode\" id=\"cb24\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb24-1\"><a href=\"#cb24-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb24-2\"><a href=\"#cb24-2\"></a></span>\n<span id=\"cb24-3\"><a href=\"#cb24-3\"></a>    frag       = mix(startFrag.xy, endFrag.xy, search1);</span>\n<span id=\"cb24-4\"><a href=\"#cb24-4\"></a>    uv.xy      = frag / texSize;</span>\n<span id=\"cb24-5\"><a href=\"#cb24-5\"></a>    positionTo = texture(positionTexture, uv.xy);</span>\n<span id=\"cb24-6\"><a href=\"#cb24-6\"></a></span>\n<span id=\"cb24-7\"><a href=\"#cb24-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>As you did in the first pass, use the current position on the ray line to sample a position from the scene.</p>\n<div class=\"sourceCode\" id=\"cb25\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb25-1\"><a href=\"#cb25-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb25-2\"><a href=\"#cb25-2\"></a></span>\n<span id=\"cb25-3\"><a href=\"#cb25-3\"></a>    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);</span>\n<span id=\"cb25-4\"><a href=\"#cb25-4\"></a>    depth        = viewDistance - positionTo.y;</span>\n<span id=\"cb25-5\"><a href=\"#cb25-5\"></a></span>\n<span id=\"cb25-6\"><a href=\"#cb25-6\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Interpolate the view distance for the current ray line position and calculate the camera distance difference between the ray at this point and the scene.</p>\n<div class=\"sourceCode\" id=\"cb26\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb26-1\"><a href=\"#cb26-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb26-2\"><a href=\"#cb26-2\"></a></span>\n<span id=\"cb26-3\"><a href=\"#cb26-3\"></a>    <span class=\"cf\">if</span> (depth &gt; <span class=\"dv\">0</span> &amp;&amp; depth &lt; thickness) {</span>\n<span id=\"cb26-4\"><a href=\"#cb26-4\"></a>      hit1 = <span class=\"dv\">1</span>;</span>\n<span id=\"cb26-5\"><a href=\"#cb26-5\"></a>      search1 = search0 + ((search1 - search0) / <span class=\"dv\">2</span>);</span>\n<span id=\"cb26-6\"><a href=\"#cb26-6\"></a>    } <span class=\"cf\">else</span> {</span>\n<span id=\"cb26-7\"><a href=\"#cb26-7\"></a>      <span class=\"dt\">float</span> temp = search1;</span>\n<span id=\"cb26-8\"><a href=\"#cb26-8\"></a>      search1 = search1 + ((search1 - search0) / <span class=\"dv\">2</span>);</span>\n<span id=\"cb26-9\"><a href=\"#cb26-9\"></a>      search0 = temp;</span>\n<span id=\"cb26-10\"><a href=\"#cb26-10\"></a>    }</span>\n<span id=\"cb26-11\"><a href=\"#cb26-11\"></a></span>\n<span id=\"cb26-12\"><a href=\"#cb26-12\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the depth is within bounds, this is a hit. Set <code>hit1</code> to one and set <code>search1</code> to be halfway between the last known miss position and this current hit position. If the depth is not within bounds, this is a miss. Set <code>search1</code> to be halfway between this current miss position and the last known hit position. Move <code>search0</code> to this current miss position. Continue this back and forth search while <code>i</code> is less than <code>steps</code>.</p>\n<div class=\"sourceCode\" id=\"cb27\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb27-1\"><a href=\"#cb27-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb27-2\"><a href=\"#cb27-2\"></a></span>\n<span id=\"cb27-3\"><a href=\"#cb27-3\"></a>  <span class=\"dt\">float</span> visibility =</span>\n<span id=\"cb27-4\"><a href=\"#cb27-4\"></a>      hit1</span>\n<span id=\"cb27-5\"><a href=\"#cb27-5\"></a></span>\n<span id=\"cb27-6\"><a href=\"#cb27-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You're now done with the second and final pass but before you can output the reflected UV coordinates, you'll need to calculate the <code>visibility</code> of the reflection. The <code>visibility</code> ranges from zero to one. If there wasn't a hit in the second pass, the <code>visibility</code> is zero.</p>\n<div class=\"sourceCode\" id=\"cb28\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb28-1\"><a href=\"#cb28-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb28-2\"><a href=\"#cb28-2\"></a></span>\n<span id=\"cb28-3\"><a href=\"#cb28-3\"></a>    * positionTo.w</span>\n<span id=\"cb28-4\"><a href=\"#cb28-4\"></a></span>\n<span id=\"cb28-5\"><a href=\"#cb28-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the reflected scene position's alpha or <code>w</code> component is zero, the <code>visibility</code> is zero. Note that if <code>w</code> is zero, there was no scene position at that point.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7e2cOdZ.gif\" alt=\"Reflection ray pointing towards the camera position.\" title=\"Reflection ray pointing towards the camera position.\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb29\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb29-1\"><a href=\"#cb29-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb29-2\"><a href=\"#cb29-2\"></a></span>\n<span id=\"cb29-3\"><a href=\"#cb29-3\"></a>    * ( <span class=\"dv\">1</span></span>\n<span id=\"cb29-4\"><a href=\"#cb29-4\"></a>      - max</span>\n<span id=\"cb29-5\"><a href=\"#cb29-5\"></a>         ( dot(-unitPositionFrom, pivot)</span>\n<span id=\"cb29-6\"><a href=\"#cb29-6\"></a>         , <span class=\"dv\">0</span></span>\n<span id=\"cb29-7\"><a href=\"#cb29-7\"></a>         )</span>\n<span id=\"cb29-8\"><a href=\"#cb29-8\"></a>      )</span>\n<span id=\"cb29-9\"><a href=\"#cb29-9\"></a></span>\n<span id=\"cb29-10\"><a href=\"#cb29-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>One of the ways in which screen space reflection can fail is when the reflection ray points in the general direction of the camera. If the reflection ray points towards the camera and hits something, it's most likely hitting the back side of something facing away from the camera.</p>\n<p>To handle this failure case, you'll need to gradually fade out the reflection based on how much the reflection vector points to the camera's position. If the reflection vector points directly in the opposite direction of the position vector, the visibility is zero. Any other direction results in the visibility being greater than zero.</p>\n<p>Remember to normalize both vectors when taking the dot product. <code>unitPositionFrom</code> is the normalized position vector. It has a length or magnitude of one.</p>\n<div class=\"sourceCode\" id=\"cb30\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb30-1\"><a href=\"#cb30-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb30-2\"><a href=\"#cb30-2\"></a></span>\n<span id=\"cb30-3\"><a href=\"#cb30-3\"></a>    * ( <span class=\"dv\">1</span></span>\n<span id=\"cb30-4\"><a href=\"#cb30-4\"></a>      - clamp</span>\n<span id=\"cb30-5\"><a href=\"#cb30-5\"></a>          ( depth / thickness</span>\n<span id=\"cb30-6\"><a href=\"#cb30-6\"></a>          , <span class=\"dv\">0</span></span>\n<span id=\"cb30-7\"><a href=\"#cb30-7\"></a>          , <span class=\"dv\">1</span></span>\n<span id=\"cb30-8\"><a href=\"#cb30-8\"></a>          )</span>\n<span id=\"cb30-9\"><a href=\"#cb30-9\"></a>      )</span>\n<span id=\"cb30-10\"><a href=\"#cb30-10\"></a></span>\n<span id=\"cb30-11\"><a href=\"#cb30-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>As you sample scene positions along the reflection ray, you're hoping to find the exact point where the reflection ray first intersects with the scene's geometry. Unfortunately, you may not find this particular point. Fade out the reflection the further it is from the intersection point you did find.</p>\n<div class=\"sourceCode\" id=\"cb31\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb31-1\"><a href=\"#cb31-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb31-2\"><a href=\"#cb31-2\"></a></span>\n<span id=\"cb31-3\"><a href=\"#cb31-3\"></a>    * ( <span class=\"dv\">1</span></span>\n<span id=\"cb31-4\"><a href=\"#cb31-4\"></a>      - clamp</span>\n<span id=\"cb31-5\"><a href=\"#cb31-5\"></a>          (   length(positionTo - positionFrom)</span>\n<span id=\"cb31-6\"><a href=\"#cb31-6\"></a>            / maxDistance</span>\n<span id=\"cb31-7\"><a href=\"#cb31-7\"></a>          , <span class=\"dv\">0</span></span>\n<span id=\"cb31-8\"><a href=\"#cb31-8\"></a>          , <span class=\"dv\">1</span></span>\n<span id=\"cb31-9\"><a href=\"#cb31-9\"></a>          )</span>\n<span id=\"cb31-10\"><a href=\"#cb31-10\"></a>      )</span>\n<span id=\"cb31-11\"><a href=\"#cb31-11\"></a></span>\n<span id=\"cb31-12\"><a href=\"#cb31-12\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Fade out the reflection based on how far way the reflected point is from the initial starting point. This will fade out the reflection instead of it ending abruptly as it reaches <code>maxDistance</code>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/i0btBna.gif\" alt=\"Reflection ray exiting the frustum.\" title=\"Reflection ray exiting the frustum.\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb32\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb32-1\"><a href=\"#cb32-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb32-2\"><a href=\"#cb32-2\"></a></span>\n<span id=\"cb32-3\"><a href=\"#cb32-3\"></a>    * (uv.x &lt; <span class=\"dv\">0</span> || uv.x &gt; <span class=\"dv\">1</span> ? <span class=\"dv\">0</span> : <span class=\"dv\">1</span>)</span>\n<span id=\"cb32-4\"><a href=\"#cb32-4\"></a>    * (uv.y &lt; <span class=\"dv\">0</span> || uv.y &gt; <span class=\"dv\">1</span> ? <span class=\"dv\">0</span> : <span class=\"dv\">1</span>);</span>\n<span id=\"cb32-5\"><a href=\"#cb32-5\"></a></span>\n<span id=\"cb32-6\"><a href=\"#cb32-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>If the reflected UV coordinates are out of bounds, set the <code>visibility</code> to zero. This occurs when the reflection ray travels outside the camera's frustum.</p>\n<div class=\"sourceCode\" id=\"cb33\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb33-1\"><a href=\"#cb33-1\"></a>  visibility = clamp(visibility, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb33-2\"><a href=\"#cb33-2\"></a></span>\n<span id=\"cb33-3\"><a href=\"#cb33-3\"></a>  uv.ba = vec2(visibility);</span></code></pre></div>\n<p>Set the blue and alpha component to the visibility as the UV coordinates only need the RG or XY components of the final vector.</p>\n<div class=\"sourceCode\" id=\"cb34\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb34-1\"><a href=\"#cb34-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb34-2\"><a href=\"#cb34-2\"></a></span>\n<span id=\"cb34-3\"><a href=\"#cb34-3\"></a>  fragColor = uv;</span>\n<span id=\"cb34-4\"><a href=\"#cb34-4\"></a></span>\n<span id=\"cb34-5\"><a href=\"#cb34-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The final fragment color is the reflected UV coordinates and the visibility.</p>\n<h3 id=\"specular-map\">Specular Map</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuFYVWB.gif\" alt=\"Specular Map\" title=\"Specular Map\">\n</p>\n\n<p>In addition to the reflected UV coordinates, you'll also need a specular map. The example code creates one using the fragment's material specular properties.</p>\n<div class=\"sourceCode\" id=\"cb35\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb35-1\"><a href=\"#cb35-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb35-2\"><a href=\"#cb35-2\"></a></span>\n<span id=\"cb35-3\"><a href=\"#cb35-3\"></a><span class=\"pp\">#define MAX_SHININESS 127.75</span></span>\n<span id=\"cb35-4\"><a href=\"#cb35-4\"></a></span>\n<span id=\"cb35-5\"><a href=\"#cb35-5\"></a>uniform <span class=\"kw\">struct</span></span>\n<span id=\"cb35-6\"><a href=\"#cb35-6\"></a>  { vec3 specular</span>\n<span id=\"cb35-7\"><a href=\"#cb35-7\"></a>  ; <span class=\"dt\">float</span> shininess</span>\n<span id=\"cb35-8\"><a href=\"#cb35-8\"></a>  ;</span>\n<span id=\"cb35-9\"><a href=\"#cb35-9\"></a>  } p3d_Material;</span>\n<span id=\"cb35-10\"><a href=\"#cb35-10\"></a></span>\n<span id=\"cb35-11\"><a href=\"#cb35-11\"></a>out vec4 fragColor;</span>\n<span id=\"cb35-12\"><a href=\"#cb35-12\"></a></span>\n<span id=\"cb35-13\"><a href=\"#cb35-13\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb35-14\"><a href=\"#cb35-14\"></a>  fragColor =</span>\n<span id=\"cb35-15\"><a href=\"#cb35-15\"></a>    vec4</span>\n<span id=\"cb35-16\"><a href=\"#cb35-16\"></a>      ( p3d_Material.specular</span>\n<span id=\"cb35-17\"><a href=\"#cb35-17\"></a>      , clamp(p3d_Material.shininess / MAX_SHININESS, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>)</span>\n<span id=\"cb35-18\"><a href=\"#cb35-18\"></a>      );</span>\n<span id=\"cb35-19\"><a href=\"#cb35-19\"></a>}</span></code></pre></div>\n<p>The specular fragment shader is quite simple. Using the fragment's material, the shader outputs the specular color and uses the alpha channel for the shininess. The shininess is mapped to a range of zero to one. In Blender, the maximum specular hardness or shininess is 511. When exporting from Blender to Panda3D, 511 is exported as 127.75. Feel free to adjust the shininess to range of zero to one however you see fit for your particular stack.</p>\n<p>The example code generates a specular map from the material specular properties but you could create one in GIMP, for example, and attach that as a texture to your 3D model. For instance, say your 3D treasure chest has shiny brackets on it but nothing else should reflect the environment. You can paint the brackets some shade of gray and the rest of the treasure chest black. This will mask off the brackets, allowing your shader to render the reflections on only the brackets and nothing else.</p>\n<h3 id=\"scene-colors\">Scene Colors</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/diBSxPI.png\" alt=\"Scene Colors\" title=\"Scene Colors\">\n</p>\n\n<p>You'll need to render the parts of the scene you wish to reflect and store this in a framebuffer texture. This is typically just the scene without any reflections.</p>\n<h3 id=\"reflected-scene-colors\">Reflected Scene Colors</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/UPvgfDU.gif\" alt=\"Reflected Scene Colors\" title=\"Reflected Scene Colors\">\n</p>\n\n<p>Here you see the reflected colors saved to a framebuffer texture.</p>\n<div class=\"sourceCode\" id=\"cb36\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb36-1\"><a href=\"#cb36-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb36-2\"><a href=\"#cb36-2\"></a></span>\n<span id=\"cb36-3\"><a href=\"#cb36-3\"></a>uniform sampler2D uvTexture;</span>\n<span id=\"cb36-4\"><a href=\"#cb36-4\"></a>uniform sampler2D colorTexture;</span>\n<span id=\"cb36-5\"><a href=\"#cb36-5\"></a></span>\n<span id=\"cb36-6\"><a href=\"#cb36-6\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Once you have the reflected UV coordinates, looking up the reflected colors is fairly easy. You'll need the reflected UV coordinates texture and the color texture containing the colors you wish to reflect.</p>\n<div class=\"sourceCode\" id=\"cb37\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb37-1\"><a href=\"#cb37-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb37-2\"><a href=\"#cb37-2\"></a></span>\n<span id=\"cb37-3\"><a href=\"#cb37-3\"></a>  vec2 texSize  = textureSize(uvTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb37-4\"><a href=\"#cb37-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb37-5\"><a href=\"#cb37-5\"></a></span>\n<span id=\"cb37-6\"><a href=\"#cb37-6\"></a>  vec4 uv    = texture(uvTexture,    texCoord);</span>\n<span id=\"cb37-7\"><a href=\"#cb37-7\"></a>  vec4 color = texture(colorTexture, uv.xy);</span>\n<span id=\"cb37-8\"><a href=\"#cb37-8\"></a></span>\n<span id=\"cb37-9\"><a href=\"#cb37-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the UV coordinates for the current fragment, look up the reflected color.</p>\n<div class=\"sourceCode\" id=\"cb38\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb38-1\"><a href=\"#cb38-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb38-2\"><a href=\"#cb38-2\"></a></span>\n<span id=\"cb38-3\"><a href=\"#cb38-3\"></a>  <span class=\"dt\">float</span> alpha = clamp(uv.b, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb38-4\"><a href=\"#cb38-4\"></a></span>\n<span id=\"cb38-5\"><a href=\"#cb38-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Recall that the reflected UV texture stored the visibility in the B or blue component. This is the alpha channel for the reflected colors framebuffer texture.</p>\n<div class=\"sourceCode\" id=\"cb39\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb39-1\"><a href=\"#cb39-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb39-2\"><a href=\"#cb39-2\"></a></span>\n<span id=\"cb39-3\"><a href=\"#cb39-3\"></a>  fragColor = vec4(mix(vec3(<span class=\"dv\">0</span>), color.rgb, alpha), alpha);</span>\n<span id=\"cb39-4\"><a href=\"#cb39-4\"></a></span>\n<span id=\"cb39-5\"><a href=\"#cb39-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The fragment color is a mix between no reflection and the reflected color based on the visibility. The visibility was computed during the reflected UV coordinates step.</p>\n<h3 id=\"blurred-reflected-scene-colors\">Blurred Reflected Scene Colors</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/gVvx1Ei.png\" alt=\"Blurred Reflected Scene Colors\" title=\"Blurred Reflected Scene Colors\">\n</p>\n\n<p>Now blur the reflected scene colors and store this in a framebuffer texture. The blurring is done using a box blur. Refer to the <a href=\"ssao.html#blurring\">SSAO blurring</a> step for details.</p>\n<p>The blurred reflected colors are used for surfaces that have a less than mirror like finish. These surfaces have tiny little hills and valleys that tend to diffuse or blur the reflection. I'll cover this more during the roughness calculation.</p>\n<h3 id=\"reflections\">Reflections</h3>\n<div class=\"sourceCode\" id=\"cb40\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb40-1\"><a href=\"#cb40-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb40-2\"><a href=\"#cb40-2\"></a></span>\n<span id=\"cb40-3\"><a href=\"#cb40-3\"></a>uniform sampler2D colorTexture;</span>\n<span id=\"cb40-4\"><a href=\"#cb40-4\"></a>uniform sampler2D colorBlurTexture;</span>\n<span id=\"cb40-5\"><a href=\"#cb40-5\"></a>uniform sampler2D specularTexture;</span>\n<span id=\"cb40-6\"><a href=\"#cb40-6\"></a></span>\n<span id=\"cb40-7\"><a href=\"#cb40-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>To generate the final reflections, you'll need the three framebuffer textures computed earlier. You'll need the reflected colors, the blurred reflected colors, and the specular map.</p>\n<div class=\"sourceCode\" id=\"cb41\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb41-1\"><a href=\"#cb41-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb41-2\"><a href=\"#cb41-2\"></a></span>\n<span id=\"cb41-3\"><a href=\"#cb41-3\"></a>  vec4 specular  = texture(specularTexture,  texCoord);</span>\n<span id=\"cb41-4\"><a href=\"#cb41-4\"></a>  vec4 color     = texture(colorTexture,     texCoord);</span>\n<span id=\"cb41-5\"><a href=\"#cb41-5\"></a>  vec4 colorBlur = texture(colorBlurTexture, texCoord);</span>\n<span id=\"cb41-6\"><a href=\"#cb41-6\"></a></span>\n<span id=\"cb41-7\"><a href=\"#cb41-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Look up the specular amount and shininess, the reflected scene color, and the blurred reflected scene color.</p>\n<div class=\"sourceCode\" id=\"cb42\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb42-1\"><a href=\"#cb42-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb42-2\"><a href=\"#cb42-2\"></a></span>\n<span id=\"cb42-3\"><a href=\"#cb42-3\"></a>  <span class=\"dt\">float</span> specularAmount = dot(specular.rgb, vec3(<span class=\"dv\">1</span>)) / <span class=\"dv\">3</span>;</span>\n<span id=\"cb42-4\"><a href=\"#cb42-4\"></a></span>\n<span id=\"cb42-5\"><a href=\"#cb42-5\"></a>  <span class=\"cf\">if</span> (specularAmount &lt;= <span class=\"dv\">0</span>) { fragColor = vec4(<span class=\"dv\">0</span>); <span class=\"cf\">return</span>; }</span>\n<span id=\"cb42-6\"><a href=\"#cb42-6\"></a></span>\n<span id=\"cb42-7\"><a href=\"#cb42-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Map the specular color to a greyscale value. If the specular amount is none, set the frag color to nothing and return.</p>\n<p>Later on, you'll multiply the final reflection color by the specular amount. Multiplying by the specular amount allows you to control how much a material reflects its environment simply by brightening or darkening the greyscale value in the specular map.</p>\n<div class=\"sourceCode\" id=\"cb43\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb43-1\"><a href=\"#cb43-1\"></a>  dot(specular.rgb, vec3(<span class=\"dv\">1</span>)) == (specular.r + specular.g + specular.b);</span></code></pre></div>\n<p>Using the dot product to produce the greyscale value is just a short way of summing the three color components.</p>\n<div class=\"sourceCode\" id=\"cb44\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb44-1\"><a href=\"#cb44-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb44-2\"><a href=\"#cb44-2\"></a></span>\n<span id=\"cb44-3\"><a href=\"#cb44-3\"></a>  <span class=\"dt\">float</span> roughness = <span class=\"dv\">1</span> - min(specular.a, <span class=\"dv\">1</span>);</span>\n<span id=\"cb44-4\"><a href=\"#cb44-4\"></a></span>\n<span id=\"cb44-5\"><a href=\"#cb44-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Calculate the roughness based on the shininess value set during the specular map step. Recall that the shininess value was saved in the alpha channel of the specular map. The shininess determined how spread out or blurred the specular reflection was. Similarly, the <code>roughness</code> determines how blurred the reflection is. A roughness of one will produce the blurred reflection color. A roughness of zero will produce the non-blurred reflection color. Doing it this way allows you to control how blurred the reflection is just by changing the material's shininess value.</p>\n<p>The example code generates a roughness map from the material specular properties but you could create one in GIMP, for example, and attach that as a texture to your 3D model. For instance, say you have a tiled floor that has polished tiles and scratched up tiles. The polished tiles could be painted a more translucent white while the scratched up tiles could be painted a more opaque white. The more translucent/transparent the greyscale value, the more the shader will use the blurred reflected color. The scratched tiles will have a blurry reflection while the polished tiles will have a mirror like reflection.</p>\n<div class=\"sourceCode\" id=\"cb45\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb45-1\"><a href=\"#cb45-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb45-2\"><a href=\"#cb45-2\"></a></span>\n<span id=\"cb45-3\"><a href=\"#cb45-3\"></a>  fragColor = mix(color, colorBlur, roughness) * specularAmount;</span>\n<span id=\"cb45-4\"><a href=\"#cb45-4\"></a></span>\n<span id=\"cb45-5\"><a href=\"#cb45-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Mix the reflected color and blurred reflected color based on the roughness. Multiply that vector by the specular amount and then set that value as the fragment color.</p>\n<p>The reflection color is a mix between the reflected scene color and the blurred reflected scene color based on the roughness. A high roughness will produce a blurry reflection meaning the surface is rough. A low roughness will produce a clear reflection meaning the surface is smooth.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/material-specular.frag\" target=\"_blank\" rel=\"noopener noreferrer\">material-specular.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/screen-space-reflection.frag\" target=\"_blank\" rel=\"noopener noreferrer\">screen-space-reflection.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/reflection-color.frag\" target=\"_blank\" rel=\"noopener noreferrer\">reflection-color.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/reflection.frag\" target=\"_blank\" rel=\"noopener noreferrer\">reflection.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag\" target=\"_blank\" rel=\"noopener noreferrer\">box-blur.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base-combine.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base-combine.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"chromatic-aberration.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"screen-space-refraction.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/screen-space-refraction.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Screen Space Refraction | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Screen Space Refraction | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Screen Space Refraction | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"screen-space-reflection.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"foam.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"screen-space-refraction-ssr\">Screen Space Refraction (SSR)</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/8Mdcn4y.gif\" alt=\"Screen Space Refraction\" title=\"Screen Space Refraction\">\n</p>\n\n<p>Screen space refraction, much like <a href=\"screen-space-reflection.html\">screen space reflection</a>, adds a touch of realism you can't find anywhere else. Glass, plastic, water, and other transparent/translucent materials spring to life.</p>\n<p><a href=\"screen-space-reflection.html\">Screen space reflection</a> and screen space refraction work almost identically expect for one major difference. Instead of using the reflected vector, screen space refraction uses the <a href=\"http://asawicki.info/news_1301_reflect_and_refract_functions.html\">refracted vector</a>. It's a slight change in code but a big difference visually.</p>\n<h3 id=\"vertex-positions\">Vertex Positions</h3>\n<p>Like SSAO, you'll need the vertex positions in view space. Referrer back to <a href=\"ssao.html#vertex-positions\">SSAO</a> for details.</p>\n<p>However, unlike SSAO, you'll need the scene's vertex positions with and without the refractive objects. Refractive surfaces are translucent, meaning you can see through them. Since you can see through them, you'll need the vertex positions behind the refractive surface. Having both the foreground and background vertex positions will allow you to calculate UV coordinates and depth.</p>\n<h3 id=\"vertex-normals\">Vertex Normals</h3>\n<p>To compute the refractions, you'll need the scene's foreground vertex normals in view space. The background vertex normals aren't needed unless you need to incorporate the background surface detail while calculating the refracted UV coordinates and distances. Referrer back to <a href=\"ssao.html#vertex-normals\">SSAO</a> for details.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MZ2R8I6.gif\" alt=\"Normal maps versus no normal maps.\" title=\"Normal maps versus no normal maps.\">\n</p>\n\n<p>Here you see the water refracting the light with and without normal maps. If available, be sure to use the normal mapped normals instead of the vertex normals. The smoother and flatter the surface, the harder it is to tell the light is being refracted. There will be some distortion but not enough to make it worthwhile.</p>\n<p>To use the normal maps instead, you'll need to transform the normal mapped normals from tangent space to view space just like you did in the <a href=\"normal-mapping.html#fragment\">lighting</a> calculations. You can see this being done in <a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a>.</p>\n<h3 id=\"position-transformations\">Position Transformations</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bXtXDyu.gif\" alt=\"Position Transformations\" title=\"Position Transformations\">\n</p>\n\n<p>Just like <a href=\"ssao.html\">SSAO</a> and <a href=\"screen-space-reflection.html\">screen space reflection</a>, screen space refraction goes back and forth between the screen and view space. You'll need the camera lens' projection matrix to transform points in view space to clip space. From clip space, you'll have to transform the points again to UV space. Once in UV space, you can sample a vertex/fragment position from the scene which will be the closest position in the scene to your sample. This is the <em>screen space</em> part in <em>screen space refraction</em> since the \"screen\" is a texture UV mapped over a screen shaped rectangle.</p>\n<h3 id=\"refracted-uv-coordinates\">Refracted UV Coordinates</h3>\n<p>Recall that UV coordinates range from zero to one for both U and V. The screen is just a 2D texture UV mapped over a screen-sized rectangle. Knowing this, the example code doesn't actually need the final rendering of the scene to compute the refraction. It can instead calculate what UV coordinate each screen pixel will eventually use. These calculated UV coordinates can be saved to a framebuffer texture and used later when the scene has been rendered.</p>\n<p>The process of refracting the UV coordinates is very similar to the process of <a href=\"screen-space-reflection.html#reflected-uv-coordinates\">reflecting the UV coordinates</a>. Below are the adjustments you'll need to turn reflection into refraction.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform sampler2D positionFromTexture;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>uniform sampler2D positionToTexture;</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>uniform sampler2D normalFromTexture;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Reflection only deals with what is in front of the reflective surface. Refraction, however, deals with what is behind the refractive surface. To accommodate this, you'll need both the vertex positions of the scene with the refracting surfaces taken out and the vertex positions of the scene with the refracting surfaces left in.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FjQtjsm.gif\" alt=\"Without and with refractive surfaces.\" title=\"Without and with refractive surfaces.\">\n</p>\n\n<p><code>positionFromTexture</code> are the scene's vertex positions with the refracting surfaces left in. <code>positionToTexture</code> are the scene's vertex positions with the refracting surfaces taken out. <code>normalFromTexture</code> are the scene's vertex normals with the refraction surfaces left in. There's no need for the vertex normals behind the refractive surfaces unless you want to incorporate the surface detail for the background geometry.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform vec2 rior;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>Refraction has one more adjustable parameter than reflection. <code>rior</code> is the relative index of refraction or relative refractive index. It is the ratio of the refraction indexes of two mediums. So for example, going from water to air is <code>1 / 1.33 ≈ 0.75</code>. The numerator is the refractive index of the medium the light is leaving and the denominator is the refractive index of the medium the light is entering. An <code>rior</code> of one means the light passes right through without being refracted or bent. As <code>rior</code> grows, the refraction will become a <a href=\"https://en.wikipedia.org/wiki/Total_internal_reflection\">reflection</a>.</p>\n<p>There's no requirement that <code>rior</code> must adhere to the real world. The demo uses <code>1.05</code>. This is completely unrealistic (light does not travel faster through water than air) but the realistic setting produced too many artifacts. In the end, the distortion only has to be believable—not realistic.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/dDOnobK.gif\" alt=\"Adjusting the relative index of refraction.\" title=\"Adjusting the relative index of refraction.\">\n</p>\n\n<p><code>rior</code> values above one tend to elongate the refraction while numbers below one tend to shrink the refraction.</p>\n<p>As it was with screen space reflection, the screen doesn't have the entire geometry of the scene. A refracted ray may march through the screen space and never hit a captured surface. Or it may hit a surface but it's the backside not captured by the camera. When this happened during reflection, the fragment was left blank. This indicated no reflection or not enough information to determine a reflection. Leaving the fragment blank was fine for reflection since the reflective surface would fill in the gaps.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/vcQDAYU.gif\" alt=\"Refraction Holes\" title=\"Refraction Holes\">\n</p>\n\n<p>For refraction, however, we must set the fragment to some UV. If the fragment is left blank, the refractive surface will contain holes that let the detail behind it come through. This would be okay for a completely transparent surface but usually the refractive surface will have some tint to it, reflection, etc.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  vec2 texSize  = textureSize(positionFromTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a>  vec4 uv = vec4(texCoord.xy, <span class=\"dv\">1</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a></span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The best choice is to select the UV as if the <code>rior</code> was one. This will leave the UV coordinate unchanged, allowing the background to show through instead of there being a hole in the refractive surface.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/9fybLUO.png\" alt=\"Refraction UV Map\" title=\"Refraction UV Map\">\n</p>\n\n<p>Here you see the refracted UV texture for the mill scene. The wheel and waterway disturb what is otherwise a smooth gradient. The disruptions shift the UV coordinates from their screen position to their refracted screen position.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>  vec3 unitPositionFrom = normalize(positionFrom.xyz);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>  vec3 normalFrom       = normalize(texture(normalFromTexture, texCoord).xyz);</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>  vec3 pivot            = normalize(refract(unitPositionFrom, normalFrom, rior.x));</span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The most important difference is the calculation of the refracted vector versus the reflected vector. Both use the unit position and normal but <code>refract</code> takes an additional parameter specifying the relative refractive index.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>    frag      += increment;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a>    uv.xy      = frag / texSize;</span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a>    positionTo = texture(positionToTexture, uv.xy);</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a></span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The <code>positionTo</code>, sampled by the <code>uv</code> coordinates, uses the <code>positionToTexture</code>. For reflection, you only need one framebuffer texture containing the scene's interpolated vertex positions in view space. However, for refraction, <code>positionToTexture</code> contains the vertex positions of the scene minus the refractive surfaces since the refraction ray typically goes behind the surface. If <code>positionFromTexture</code> and <code>positionToTexture</code> were the same for refraction, the refracted ray would hit the refractive surface instead of what is behind it.</p>\n<h3 id=\"refraction-mask\">Refraction Mask</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuFYVWB.gif\" alt=\"Material Specular\" title=\"Material Specular\">\n</p>\n\n<p>You'll need a mask to filter out the non-refractive parts of the scene. This mask will determine which fragment does and does not receive a refracted color. You could use this mask during the refracted UV calculation step or later when you actually sample the colors at the refracted UV coordinates.</p>\n<p>The mill scene uses the models' material specular as a mask. For the demo's purposes, the specular map is sufficient but you may want to use a more specialized map. Refer back to <a href=\"screen-space-reflection.html#specular-map\">screen space reflection</a> for how to render the specular map.</p>\n<h2 id=\"background-colors\">Background Colors</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AmT9RrU.gif\" alt=\"Background Colors\" title=\"Background Colors\">\n</p>\n\n<p>You'll need to render the parts of the scene behind the refractive objects. This can be done by hiding the refractive objects and then rendering the scene to a framebuffer texture.</p>\n<h3 id=\"foreground-colors\">Foreground Colors</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/6RPHULr.gif\" alt=\"Foreground Colors\" title=\"Foreground Colors\">\n</p>\n\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a><span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>uniform sampler2D uvTexture;</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>uniform sampler2D refractionMaskTexture;</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>uniform sampler2D positionFromTexture;</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>uniform sampler2D positionToTexture;</span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>uniform sampler2D backgroundColorTexture;</span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a></span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a><span class=\"co\">// ...</span></span></code></pre></div>\n<p>To render the actual refractions or foreground colors, you'll need the refracted UV coordinates, refraction mask, the foreground and background vertex positions, and the background colors.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  vec3  tintColor = vec3(<span class=\"fl\">0.27</span>, <span class=\"fl\">0.58</span>, <span class=\"fl\">0.92</span>, <span class=\"fl\">0.3</span>);</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>  <span class=\"dt\">float</span> depthMax  = <span class=\"dv\">2</span>;</span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a></span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p><code>tintColor</code> and <code>depthMax</code> are adjustable parameters. <code>tintColor</code> colorizes the background color. <code>depthMax</code> ranges from zero to infinity. When the distance between the foreground and background position reaches <code>depthMax</code>, the foreground color will be the fully tinted background color. At distance zero, the foreground will be the background color.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a></span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>  vec2 texSize  = textureSize(backgroundColorTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a></span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>  vec4 uv              = texture(uvTexture,              texCoord);</span>\n<span id=\"cb8-7\"><a href=\"#cb8-7\"></a>  vec4 mask            = texture(maskTexture,            texCoord);</span>\n<span id=\"cb8-8\"><a href=\"#cb8-8\"></a>  vec4 positionFrom    = texture(positionFromTexture,    texCoord);</span>\n<span id=\"cb8-9\"><a href=\"#cb8-9\"></a>  vec4 positionTo      = texture(positionToTexture,      uv.xy);</span>\n<span id=\"cb8-10\"><a href=\"#cb8-10\"></a>  vec4 backgroundColor = texture(backgroundColorTexture, uv.xy);</span>\n<span id=\"cb8-11\"><a href=\"#cb8-11\"></a></span>\n<span id=\"cb8-12\"><a href=\"#cb8-12\"></a>  <span class=\"cf\">if</span> (refractionMask.r &lt;= <span class=\"dv\">0</span>) { fragColor = vec4(<span class=\"dv\">0</span>); <span class=\"cf\">return</span>; }</span>\n<span id=\"cb8-13\"><a href=\"#cb8-13\"></a></span>\n<span id=\"cb8-14\"><a href=\"#cb8-14\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Pull out the uv coordinates, mask, background position, foreground position, and the background color.</p>\n<p>If the refraction mask is turned off for this fragment, return nothing.</p>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  <span class=\"dt\">float</span> depth   = length(positionTo.xyz - positionFrom.xyz);</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a>  <span class=\"dt\">float</span> mixture = clamp(depth / depthMax, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>);</span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a></span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\"></a>  vec3 shallowColor    = backgroundColor.rgb;</span>\n<span id=\"cb9-7\"><a href=\"#cb9-7\"></a>  vec3 deepColor       = mix(shallowColor, tintColor.rgb, tintColor.a);</span>\n<span id=\"cb9-8\"><a href=\"#cb9-8\"></a>  vec3 foregroundColor = mix(shallowColor, deepColor,     mixture);</span>\n<span id=\"cb9-9\"><a href=\"#cb9-9\"></a></span>\n<span id=\"cb9-10\"><a href=\"#cb9-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IEFKerB.gif\" alt=\"Refraction Depth\" title=\"Refraction Depth\">\n</p>\n\n<p>Calculate the depth or distance between the foreground position and the background position. At zero depth, the foreground color will be the shallow color. At <code>depthMax</code>, the foreground color will be the deep color. The deep color is the background color tinted with <code>tintColor</code>.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  fragColor = mix(vec4(<span class=\"dv\">0</span>), vec4(foregroundColor, <span class=\"dv\">1</span>), uv.b);</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a></span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Recall that the blue channel, in the refracted UV texture, is set to the visibility. The visibility declines as the refracted ray points back at the camera. While the visibility should always be one, it is put here for completeness. As the visibility lessens, the fragment color will receive less and less of the foreground color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/material-specular.frag\" target=\"_blank\" rel=\"noopener noreferrer\">material-specular.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/screen-space-refraction.frag\" target=\"_blank\" rel=\"noopener noreferrer\">screen-space-refraction.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/refraction.frag\" target=\"_blank\" rel=\"noopener noreferrer\">refraction.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base-combine.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base-combine.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"screen-space-reflection.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"foam.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/setup.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Setup | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Setup | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Setup | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"gamma-correction.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"building-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"setup\">Setup</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fYpIWNk.gif\" alt=\"Setup\" title=\"Setup\">\n</p>\n\n<p>Below is the setup used to develop and test the example code.</p>\n<h3 id=\"environment\">Environment</h3>\n<p>The example code was developed and tested using the following environment.</p>\n<ul>\n<li>Linux manjaro 5.10.42-1-MANJARO</li>\n<li>OpenGL renderer string: GeForce GTX 970/PCIe/SSE2</li>\n<li>OpenGL version string: 4.6.0 NVIDIA 465.31</li>\n<li>g++ (GCC) 11.1.0</li>\n<li>Panda3D 1.10.9</li>\n</ul>\n<h3 id=\"materials\">Materials</h3>\n<p>Each <a href=\"https://blender.org\">Blender</a> material used to build <code>mill-scene.egg</code> has five textures in the following order.</p>\n<ul>\n<li>Diffuse</li>\n<li>Normal</li>\n<li>Specular</li>\n<li>Reflection</li>\n<li>Refraction</li>\n</ul>\n<p>By having the same maps in the same positions for all models, the shaders can be generalized, reducing the need to duplicate code.</p>\n<p>If an object uses its vertex normals, a \"flat blue\" normal map is used.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tFmKgoH.png\" alt=\"A flat normal map.\" title=\"A flat normal map.\">\n</p>\n\n<p>Here is an example of a flat normal map. The only color it contains is flat blue <code>(red = 128, green = 128, blue = 255)</code>. This color represents a unit (length one) normal pointing in the positive z-axis <code>(0, 0, 1)</code>.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>(<span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>) =</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a>  ( round((<span class=\"dv\">0</span> * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>) * <span class=\"dv\">255</span>)</span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  , round((<span class=\"dv\">0</span> * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>) * <span class=\"dv\">255</span>)</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  , round((<span class=\"dv\">1</span> * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>) * <span class=\"dv\">255</span>)</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  ) =</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a>    (<span class=\"dv\">128</span>, <span class=\"dv\">128</span>, <span class=\"dv\">255</span>) =</span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>      ( round(<span class=\"dv\">128</span> / <span class=\"dv\">255</span> * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>)</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a>      , round(<span class=\"dv\">128</span> / <span class=\"dv\">255</span> * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>)</span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a>      , round(<span class=\"dv\">255</span> / <span class=\"dv\">255</span> * <span class=\"dv\">2</span> - <span class=\"dv\">1</span>)</span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a>      ) =</span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a>        (<span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">1</span>)</span></code></pre></div>\n<p>Here you see the unit normal <code>(0, 0, 1)</code> converted to flat blue <code>(128, 128, 255)</code> and flat blue converted to the unit normal. You'll learn more about this in the <a href=\"normal-mapping.html\">normal mapping</a> technique.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/R9FgZKx.png\" alt=\"Specular Map\" title=\"Specular Map\">\n</p>\n\n<p>Up above is one of the specular maps used. The red and blue channel work to control the amount of specular reflection seen based on the camera angle. The green channel controls the shininess factor. You'll learn more about this in the <a href=\"lighting.html\">lighting</a> and <a href=\"fresnel-factor.html\">fresnel factor</a> sections.</p>\n<p>The reflection and refraction textures mask off the objects that are either reflective, refractive, or both. For the reflection texture, the red channel controls the amount of reflection and the green channel controls how clear or blurry the reflection is.</p>\n<h3 id=\"panda3d\">Panda3D</h3>\n<p>The example code uses <a href=\"https://www.panda3d.org/\">Panda3D</a> as the glue between the shaders. This has no real influence over the techniques described, meaning you'll be able to take what you learn here and apply it to your stack or game engine of choice. Panda3D does provide some conveniences. I have pointed these out so you can either find an equivalent convenience provided by your stack or replicate it yourself, if your stack doesn't provide something equivalent.</p>\n<p>Three Panda3D configurations were changed for the purposes of the demo program. You can find these in <a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/config.prc\">config.prc</a>. The configurations changed were <code>gl-coordinate-system default</code>, <code>textures-power-2 down</code>, and <code>textures-auto-power-2 1</code>. Refer to the <a href=\"http://www.panda3d.org/manual/?title=Configuring_Panda3D\" target=\"_blank\" rel=\"noopener noreferrer\">Panda3D configuration</a> page in the manual for more details.</p>\n<p>Panda3D defaults to a z-up, right-handed coordinate system while OpenGL uses a y-up, right-handed system. <code>gl-coordinate-system default</code> keeps you from having to translate between the two inside your shaders. <code>textures-auto-power-2 1</code> allows us to use texture sizes that are not a power of two if the system supports it. This comes in handy when doing SSAO and other screen/window sized related techniques since the screen/window size is usually not a power of two. <code>textures-power-2 down</code> downsizes our textures to a power of two if the system only supports texture sizes being a power of two.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"gamma-correction.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"building-the-demo.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/sharpen.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Sharpen | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Sharpen | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Sharpen | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"pixelization.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"dilation.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"sharpen\">Sharpen</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/VFDKNvl.gif\" alt=\"Sharpen\" title=\"Sharpen\">\n</p>\n\n<p>The sharpen effect increases the contrast at the edges of the image. This comes in handy when your graphics are bit too soft.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>  <span class=\"dt\">float</span> amount = <span class=\"fl\">0.8</span>;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>You can control how sharp the result is by adjusting the amount. An amount of zero leaves the image untouched. Try negative values for an odd look.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>  <span class=\"dt\">float</span> neighbor = amount * -<span class=\"dv\">1</span>;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a>  <span class=\"dt\">float</span> center   = amount * <span class=\"dv\">4</span> + <span class=\"dv\">1</span>;</span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a></span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Neighboring fragments are multiplied by <code>amount * -1</code>. The current fragment is multiplied by <code>amount * 4 + 1</code>.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  vec3 color =</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a>        texture(sharpenTexture, vec2(gl_FragCoord.x + <span class=\"dv\">0</span>, gl_FragCoord.y + <span class=\"dv\">1</span>) / texSize).rgb</span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>      * neighbor</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>      + texture(sharpenTexture, vec2(gl_FragCoord.x - <span class=\"dv\">1</span>, gl_FragCoord.y + <span class=\"dv\">0</span>) / texSize).rgb</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>      * neighbor</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>      + texture(sharpenTexture, vec2(gl_FragCoord.x + <span class=\"dv\">0</span>, gl_FragCoord.y + <span class=\"dv\">0</span>) / texSize).rgb</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>      * center</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a>      + texture(sharpenTexture, vec2(gl_FragCoord.x + <span class=\"dv\">1</span>, gl_FragCoord.y + <span class=\"dv\">0</span>) / texSize).rgb</span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>      * neighbor</span>\n<span id=\"cb3-13\"><a href=\"#cb3-13\"></a></span>\n<span id=\"cb3-14\"><a href=\"#cb3-14\"></a>      + texture(sharpenTexture, vec2(gl_FragCoord.x + <span class=\"dv\">0</span>, gl_FragCoord.y - <span class=\"dv\">1</span>) / texSize).rgb</span>\n<span id=\"cb3-15\"><a href=\"#cb3-15\"></a>      * neighbor</span>\n<span id=\"cb3-16\"><a href=\"#cb3-16\"></a>      ;</span>\n<span id=\"cb3-17\"><a href=\"#cb3-17\"></a></span>\n<span id=\"cb3-18\"><a href=\"#cb3-18\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The neighboring fragments are up, down, left, and right. After multiplying both the neighbors and the current fragment by their particular values, sum the result.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a></span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>    fragColor = vec4(color, texture(sharpenTexture, texCoord).a);</span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a></span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>This sum is the final fragment color.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/sharpen.frag\" target=\"_blank\" rel=\"noopener noreferrer\">sharpen.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"pixelization.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"dilation.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/ssao.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"SSAO | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"SSAO | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>SSAO | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"bloom.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"motion-blur.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"screen-space-ambient-occlusion-ssao\">Screen Space Ambient Occlusion (SSAO)</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/o7lCukD.gif\" alt=\"SSAO\" title=\"SSAO\">\n</p>\n\n<p>SSAO is one of those effects you never knew you needed and can't live without once you have it. It can take a scene from mediocre to wow! For fairly static scenes, you can bake ambient occlusion into a texture but for more dynamic scenes, you'll need a shader. SSAO is one of the more fairly involved shading techniques, but once you pull it off, you'll feel like a shader master.</p>\n<p>By using only a handful of textures, SSAO can approximate the <a href=\"https://en.wikipedia.org/wiki/Ambient_occlusion\">ambient occlusion</a> of a scene. This is faster than trying to compute the ambient occlusion by going through all of the scene's geometry. These handful of textures all originate in screen space giving screen space ambient occlusion its name.</p>\n<h3 id=\"inputs\">Inputs</h3>\n<p>The SSAO shader will need the following inputs.</p>\n<ul>\n<li>Vertex position vectors in view space.</li>\n<li>Vertex normal vectors in view space.</li>\n<li>Sample vectors in tangent space.</li>\n<li>Noise vectors in tangent space.</li>\n<li>The camera lens' projection matrix.</li>\n</ul>\n<h3 id=\"vertex-positions\">Vertex Positions</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/gr7IxKv.png\" alt=\"Panda3D Vertex Positions\" title=\"Panda3D Vertex Positions\">\n</p>\n\n<p>Storing the vertex positions into a framebuffer texture is not a necessity. You can recreate them from the <a href=\"http://theorangeduck.com/page/pure-depth-ssao\">camera's depth buffer</a>. This being a beginners guide, I'll avoid this optimization and keep it straight forward. Feel free to use the depth buffer, however, for your implementation.</p>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode cpp\"><code class=\"sourceCode cpp\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a>PT(Texture) depthTexture =</span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a>  <span class=\"kw\">new</span> Texture(<span class=\"st\">&quot;depthTexture&quot;</span>);</span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>depthTexture-&gt;set_format</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a>  ( Texture::Format::F_depth_component32</span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>  );</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>PT(GraphicsOutput) depthBuffer =</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a>  graphicsOutput-&gt;make_texture_buffer</span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a>    ( <span class=\"st\">&quot;depthBuffer&quot;</span></span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a>    , <span class=\"dv\">0</span></span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a>    , <span class=\"dv\">0</span></span>\n<span id=\"cb1-12\"><a href=\"#cb1-12\"></a>    , depthTexture</span>\n<span id=\"cb1-13\"><a href=\"#cb1-13\"></a>    );</span>\n<span id=\"cb1-14\"><a href=\"#cb1-14\"></a>depthBuffer-&gt;set_clear_color</span>\n<span id=\"cb1-15\"><a href=\"#cb1-15\"></a>  ( LVecBase4f(<span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">0</span>, <span class=\"dv\">0</span>)</span>\n<span id=\"cb1-16\"><a href=\"#cb1-16\"></a>  );</span>\n<span id=\"cb1-17\"><a href=\"#cb1-17\"></a></span>\n<span id=\"cb1-18\"><a href=\"#cb1-18\"></a>NodePath depthCameraNP =</span>\n<span id=\"cb1-19\"><a href=\"#cb1-19\"></a>  window-&gt;make_camera();</span>\n<span id=\"cb1-20\"><a href=\"#cb1-20\"></a>DCAST(Camera, depthCameraNP.node())-&gt;set_lens</span>\n<span id=\"cb1-21\"><a href=\"#cb1-21\"></a>  ( window-&gt;get_camera(<span class=\"dv\">0</span>)-&gt;get_lens()</span>\n<span id=\"cb1-22\"><a href=\"#cb1-22\"></a>  );</span>\n<span id=\"cb1-23\"><a href=\"#cb1-23\"></a>PT(DisplayRegion) depthBufferRegion =</span>\n<span id=\"cb1-24\"><a href=\"#cb1-24\"></a>  depthBuffer-&gt;make_display_region</span>\n<span id=\"cb1-25\"><a href=\"#cb1-25\"></a>    ( <span class=\"dv\">0</span></span>\n<span id=\"cb1-26\"><a href=\"#cb1-26\"></a>    , <span class=\"dv\">1</span></span>\n<span id=\"cb1-27\"><a href=\"#cb1-27\"></a>    , <span class=\"dv\">0</span></span>\n<span id=\"cb1-28\"><a href=\"#cb1-28\"></a>    , <span class=\"dv\">1</span></span>\n<span id=\"cb1-29\"><a href=\"#cb1-29\"></a>    );</span>\n<span id=\"cb1-30\"><a href=\"#cb1-30\"></a>depthBufferRegion-&gt;set_camera(depthCameraNP);</span></code></pre></div>\n<p>If you do decide to use the depth buffer, here's how you can set it up using Panda3D.</p>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a>in vec4 vertexPosition;</span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>out vec4 fragColor;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a>  fragColor = vertexPosition;</span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>}</span></code></pre></div>\n<p>Here's the simple shader used to render out the view space vertex positions into a framebuffer texture. The more involved work is setting up the framebuffer texture such that the fragment vector components it receives are not clamped to <code>[0, 1]</code> and that each one has a high enough precision (a high enough number of bits). For example, if a particular interpolated vertex position is <code>&lt;-139.444444566, 0.00000034343, 2.5&gt;</code>, you don't want it stored into the texture as <code>&lt;0.0, 0.0, 1.0&gt;</code>.</p>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>  FrameBufferProperties fbp = FrameBufferProperties::get_default();</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a>  fbp.set_rgba_bits(<span class=\"dv\">32</span>, <span class=\"dv\">32</span>, <span class=\"dv\">32</span>, <span class=\"dv\">32</span>);</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>  fbp.set_rgb_color(true);</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  fbp.set_float_color(true);</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a></span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Here's how the example code sets up the framebuffer texture to store the vertex positions. It wants 32 bits per red, green, blue, and alpha components and disables clamping the values to <code>[0, 1]</code> The <code>set_rgba_bits(32, 32, 32, 32)</code> call sets the bits and also disables the clamping.</p>\n<div class=\"sourceCode\" id=\"cb4\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb4-1\"><a href=\"#cb4-1\"></a>  glTexImage2D</span>\n<span id=\"cb4-2\"><a href=\"#cb4-2\"></a>    ( GL_TEXTURE_2D</span>\n<span id=\"cb4-3\"><a href=\"#cb4-3\"></a>    , <span class=\"dv\">0</span></span>\n<span id=\"cb4-4\"><a href=\"#cb4-4\"></a>    , GL_RGB32F</span>\n<span id=\"cb4-5\"><a href=\"#cb4-5\"></a>    , <span class=\"dv\">1200</span></span>\n<span id=\"cb4-6\"><a href=\"#cb4-6\"></a>    , <span class=\"dv\">900</span></span>\n<span id=\"cb4-7\"><a href=\"#cb4-7\"></a>    , <span class=\"dv\">0</span></span>\n<span id=\"cb4-8\"><a href=\"#cb4-8\"></a>    , GL_RGB</span>\n<span id=\"cb4-9\"><a href=\"#cb4-9\"></a>    , GL_FLOAT</span>\n<span id=\"cb4-10\"><a href=\"#cb4-10\"></a>    , nullptr</span>\n<span id=\"cb4-11\"><a href=\"#cb4-11\"></a>    );</span></code></pre></div>\n<p>Here's the equivalent OpenGL call. <code>GL_RGB32F</code> sets the bits and also disables the clamping.</p>\n<blockquote>\nIf the color buffer is fixed-point, the components of the source and destination\nvalues and blend factors are each clamped to [0, 1] or [−1, 1] respectively for\nan unsigned normalized or signed normalized color buffer prior to evaluating the blend\nequation.\nIf the color buffer is floating-point, no clamping occurs.\n<br>\n<br>\n<footer>\n<a href=\"https://www.khronos.org/registry/OpenGL/specs/gl/glspec44.core.pdf\">Source</a>\n</footer>\n</blockquote>\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/V4nETME.png\" alt=\"OpenGL Vertex Positions\" title=\"OpenGL Vertex Positions\">\n</p>\n\n<p>Here you see the vertex positions with y being the up vector.</p>\n<p>Recall that Panda3D sets z as the up vector but OpenGL uses y as the up vector. The position shader outputs the vertex positions with z being up since Panda3D was configured with <code>gl-coordinate-system default</code>.</p>\n<h3 id=\"vertex-normals\">Vertex Normals</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ilnbkzq.gif\" alt=\"Panda3d Vertex Normals\" title=\"Panda3d Vertex Normals\">\n</p>\n\n<p>You'll need the vertex normals to correctly orient the samples you'll take in the SSAO shader. The example code generates multiple sample vectors distributed in a hemisphere but you could use a sphere and do away with the need for normals all together.</p>\n<div class=\"sourceCode\" id=\"cb5\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb5-1\"><a href=\"#cb5-1\"></a>in vec3 vertexNormal;</span>\n<span id=\"cb5-2\"><a href=\"#cb5-2\"></a></span>\n<span id=\"cb5-3\"><a href=\"#cb5-3\"></a>out vec4 fragColor;</span>\n<span id=\"cb5-4\"><a href=\"#cb5-4\"></a></span>\n<span id=\"cb5-5\"><a href=\"#cb5-5\"></a><span class=\"dt\">void</span> main() {</span>\n<span id=\"cb5-6\"><a href=\"#cb5-6\"></a>  vec3 normal = normalize(vertexNormal);</span>\n<span id=\"cb5-7\"><a href=\"#cb5-7\"></a></span>\n<span id=\"cb5-8\"><a href=\"#cb5-8\"></a>  fragColor = vec4(normal, <span class=\"dv\">1</span>);</span>\n<span id=\"cb5-9\"><a href=\"#cb5-9\"></a>}</span></code></pre></div>\n<p>Like the position shader, the normal shader is simple as well. Be sure to normalize the vertex normal and remember that they are in view space.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ucdx9Kp.gif\" alt=\"OpenGL Vertex Normals\" title=\"OpenGL Vertex Normals\">\n</p>\n\n<p>Here you see the vertex normals with y being the up vector.</p>\n<p>Recall that Panda3D sets z as the up vector but OpenGL uses y as the up vector. The normal shader outputs the vertex positions with z being up since Panda3D was configured with <code>gl-coordinate-system default</code>.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fiHXBex.gif\" alt=\"SSAO using the normal maps.\" title=\"SSAO using the normal maps.\">\n</p>\n\n<p>Here you see SSAO being used with the normal maps instead of the vertex normals. This adds an extra level of detail and will pair nicely with the normal mapped lighting.</p>\n<div class=\"sourceCode\" id=\"cb6\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb6-1\"><a href=\"#cb6-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb6-2\"><a href=\"#cb6-2\"></a></span>\n<span id=\"cb6-3\"><a href=\"#cb6-3\"></a>    normal =</span>\n<span id=\"cb6-4\"><a href=\"#cb6-4\"></a>      normalize</span>\n<span id=\"cb6-5\"><a href=\"#cb6-5\"></a>        ( normalTex.rgb</span>\n<span id=\"cb6-6\"><a href=\"#cb6-6\"></a>        * <span class=\"fl\">2.0</span></span>\n<span id=\"cb6-7\"><a href=\"#cb6-7\"></a>        - <span class=\"fl\">1.0</span></span>\n<span id=\"cb6-8\"><a href=\"#cb6-8\"></a>        );</span>\n<span id=\"cb6-9\"><a href=\"#cb6-9\"></a>    normal =</span>\n<span id=\"cb6-10\"><a href=\"#cb6-10\"></a>      normalize</span>\n<span id=\"cb6-11\"><a href=\"#cb6-11\"></a>        ( mat3</span>\n<span id=\"cb6-12\"><a href=\"#cb6-12\"></a>            ( tangent</span>\n<span id=\"cb6-13\"><a href=\"#cb6-13\"></a>            , binormal</span>\n<span id=\"cb6-14\"><a href=\"#cb6-14\"></a>            , vertexNormal</span>\n<span id=\"cb6-15\"><a href=\"#cb6-15\"></a>            )</span>\n<span id=\"cb6-16\"><a href=\"#cb6-16\"></a>        * normal</span>\n<span id=\"cb6-17\"><a href=\"#cb6-17\"></a>        );</span>\n<span id=\"cb6-18\"><a href=\"#cb6-18\"></a></span>\n<span id=\"cb6-19\"><a href=\"#cb6-19\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To use the normal maps instead, you'll need to transform the normal mapped normals from tangent space to view space just like you did in the lighting calculations.</p>\n<h3 id=\"samples\">Samples</h3>\n<p>To determine the amount of ambient occlusion for any particular fragment, you'll need to sample the surrounding area. The more samples you use, the better the approximation at the cost of performance.</p>\n<div class=\"sourceCode\" id=\"cb7\"><pre class=\"sourceCode cpp\"><code class=\"sourceCode cpp\"><span id=\"cb7-1\"><a href=\"#cb7-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb7-2\"><a href=\"#cb7-2\"></a></span>\n<span id=\"cb7-3\"><a href=\"#cb7-3\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; numberOfSamples; ++i) {</span>\n<span id=\"cb7-4\"><a href=\"#cb7-4\"></a>    LVecBase3f sample =</span>\n<span id=\"cb7-5\"><a href=\"#cb7-5\"></a>      LVecBase3f</span>\n<span id=\"cb7-6\"><a href=\"#cb7-6\"></a>        ( randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb7-7\"><a href=\"#cb7-7\"></a>        , randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb7-8\"><a href=\"#cb7-8\"></a>        , randomFloats(generator)</span>\n<span id=\"cb7-9\"><a href=\"#cb7-9\"></a>        ).normalized();</span>\n<span id=\"cb7-10\"><a href=\"#cb7-10\"></a></span>\n<span id=\"cb7-11\"><a href=\"#cb7-11\"></a>    <span class=\"dt\">float</span> rand = randomFloats(generator);</span>\n<span id=\"cb7-12\"><a href=\"#cb7-12\"></a>    sample[<span class=\"dv\">0</span>] *= rand;</span>\n<span id=\"cb7-13\"><a href=\"#cb7-13\"></a>    sample[<span class=\"dv\">1</span>] *= rand;</span>\n<span id=\"cb7-14\"><a href=\"#cb7-14\"></a>    sample[<span class=\"dv\">2</span>] *= rand;</span>\n<span id=\"cb7-15\"><a href=\"#cb7-15\"></a></span>\n<span id=\"cb7-16\"><a href=\"#cb7-16\"></a>    <span class=\"dt\">float</span> scale = (<span class=\"dt\">float</span>) i / (<span class=\"dt\">float</span>) numberOfSamples;</span>\n<span id=\"cb7-17\"><a href=\"#cb7-17\"></a>    scale = lerp(<span class=\"fl\">0.1</span>, <span class=\"fl\">1.0</span>, scale * scale);</span>\n<span id=\"cb7-18\"><a href=\"#cb7-18\"></a>    sample[<span class=\"dv\">0</span>] *= scale;</span>\n<span id=\"cb7-19\"><a href=\"#cb7-19\"></a>    sample[<span class=\"dv\">1</span>] *= scale;</span>\n<span id=\"cb7-20\"><a href=\"#cb7-20\"></a>    sample[<span class=\"dv\">2</span>] *= scale;</span>\n<span id=\"cb7-21\"><a href=\"#cb7-21\"></a></span>\n<span id=\"cb7-22\"><a href=\"#cb7-22\"></a>    ssaoSamples.push_back(sample);</span>\n<span id=\"cb7-23\"><a href=\"#cb7-23\"></a>  }</span>\n<span id=\"cb7-24\"><a href=\"#cb7-24\"></a></span>\n<span id=\"cb7-25\"><a href=\"#cb7-25\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The example code generates a number of random samples distributed in a hemisphere. These <code>ssaoSamples</code> will be sent to the SSAO shader.</p>\n<div class=\"sourceCode\" id=\"cb8\"><pre class=\"sourceCode cpp\"><code class=\"sourceCode cpp\"><span id=\"cb8-1\"><a href=\"#cb8-1\"></a>    LVecBase3f sample =</span>\n<span id=\"cb8-2\"><a href=\"#cb8-2\"></a>      LVecBase3f</span>\n<span id=\"cb8-3\"><a href=\"#cb8-3\"></a>        ( randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb8-4\"><a href=\"#cb8-4\"></a>        , randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb8-5\"><a href=\"#cb8-5\"></a>        , randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb8-6\"><a href=\"#cb8-6\"></a>        ).normalized();</span></code></pre></div>\n<p>If you'd like to distribute your samples in a sphere instead, change the random <code>z</code> component to range from negative one to one.</p>\n<h3 id=\"noise\">Noise</h3>\n<div class=\"sourceCode\" id=\"cb9\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb9-1\"><a href=\"#cb9-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb9-2\"><a href=\"#cb9-2\"></a></span>\n<span id=\"cb9-3\"><a href=\"#cb9-3\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; numberOfNoise; ++i) {</span>\n<span id=\"cb9-4\"><a href=\"#cb9-4\"></a>    LVecBase3f noise =</span>\n<span id=\"cb9-5\"><a href=\"#cb9-5\"></a>      LVecBase3f</span>\n<span id=\"cb9-6\"><a href=\"#cb9-6\"></a>        ( randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb9-7\"><a href=\"#cb9-7\"></a>        , randomFloats(generator) * <span class=\"fl\">2.0</span> - <span class=\"fl\">1.0</span></span>\n<span id=\"cb9-8\"><a href=\"#cb9-8\"></a>        , <span class=\"fl\">0.0</span></span>\n<span id=\"cb9-9\"><a href=\"#cb9-9\"></a>        );</span>\n<span id=\"cb9-10\"><a href=\"#cb9-10\"></a></span>\n<span id=\"cb9-11\"><a href=\"#cb9-11\"></a>    ssaoNoise.push_back(noise);</span>\n<span id=\"cb9-12\"><a href=\"#cb9-12\"></a>  }</span>\n<span id=\"cb9-13\"><a href=\"#cb9-13\"></a></span>\n<span id=\"cb9-14\"><a href=\"#cb9-14\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>To get a good sweep of the sampled area, you'll need to generate some noise vectors. These noise vectors will randomly tilt the hemisphere around the current fragment.</p>\n<h3 id=\"ambient-occlusion\">Ambient Occlusion</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/KKt74VE.gif\" alt=\"SSAO Texture\" title=\"SSAO Texture\">\n</p>\n\n<p>SSAO works by sampling the view space around a fragment. The more samples that are below a surface, the darker the fragment color. These samples are positioned at the fragment and pointed in the general direction of the vertex normal. Each sample is used to look up a position in the position framebuffer texture. The position returned is compared to the sample. If the sample is farther away from the camera than the position, the sample counts towards the fragment being occluded.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Nm4CJDN.gif\" alt=\"SSAO Sampling\" title=\"SSAO Sampling\">\n</p>\n\n<p>Here you see the space above the surface being sampled for occlusion.</p>\n<div class=\"sourceCode\" id=\"cb10\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb10-1\"><a href=\"#cb10-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb10-2\"><a href=\"#cb10-2\"></a></span>\n<span id=\"cb10-3\"><a href=\"#cb10-3\"></a>  <span class=\"dt\">float</span> radius    = <span class=\"dv\">1</span>;</span>\n<span id=\"cb10-4\"><a href=\"#cb10-4\"></a>  <span class=\"dt\">float</span> bias      = <span class=\"fl\">0.01</span>;</span>\n<span id=\"cb10-5\"><a href=\"#cb10-5\"></a>  <span class=\"dt\">float</span> magnitude = <span class=\"fl\">1.5</span>;</span>\n<span id=\"cb10-6\"><a href=\"#cb10-6\"></a>  <span class=\"dt\">float</span> contrast  = <span class=\"fl\">1.5</span>;</span>\n<span id=\"cb10-7\"><a href=\"#cb10-7\"></a></span>\n<span id=\"cb10-8\"><a href=\"#cb10-8\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Like some of the other techniques, the SSAO shader has a few control knobs you can tweak to get the exact look you're going for. The <code>bias</code> adds to the sample's distance from the camera. You can use the bias to combat \"acne\". The <code>radius</code> increases or decreases the coverage area of the sample space. The <code>magnitude</code> either lightens or darkens the occlusion map. The <code>contrast</code> either washes out or increases the starkness of the occlusion map.</p>\n<div class=\"sourceCode\" id=\"cb11\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb11-1\"><a href=\"#cb11-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb11-2\"><a href=\"#cb11-2\"></a></span>\n<span id=\"cb11-3\"><a href=\"#cb11-3\"></a>  vec4 position =           texture(positionTexture, texCoord);</span>\n<span id=\"cb11-4\"><a href=\"#cb11-4\"></a>  vec3 normal   = normalize(texture(normalTexture,   texCoord).xyz);</span>\n<span id=\"cb11-5\"><a href=\"#cb11-5\"></a></span>\n<span id=\"cb11-6\"><a href=\"#cb11-6\"></a>  <span class=\"dt\">int</span>  noiseX = <span class=\"dt\">int</span>(gl_FragCoord.x - <span class=\"fl\">0.5</span>) % <span class=\"dv\">4</span>;</span>\n<span id=\"cb11-7\"><a href=\"#cb11-7\"></a>  <span class=\"dt\">int</span>  noiseY = <span class=\"dt\">int</span>(gl_FragCoord.y - <span class=\"fl\">0.5</span>) % <span class=\"dv\">4</span>;</span>\n<span id=\"cb11-8\"><a href=\"#cb11-8\"></a>  vec3 random = noise[noiseX + (noiseY * <span class=\"dv\">4</span>)];</span>\n<span id=\"cb11-9\"><a href=\"#cb11-9\"></a></span>\n<span id=\"cb11-10\"><a href=\"#cb11-10\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Retrieve the position, normal, and random vector for later use. Recall that the example code created a set number of random vectors. The random vector is chosen based on the current fragment's screen position.</p>\n<div class=\"sourceCode\" id=\"cb12\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb12-1\"><a href=\"#cb12-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb12-2\"><a href=\"#cb12-2\"></a></span>\n<span id=\"cb12-3\"><a href=\"#cb12-3\"></a>  vec3 tangent  = normalize(random - normal * dot(random, normal));</span>\n<span id=\"cb12-4\"><a href=\"#cb12-4\"></a>  vec3 binormal = cross(normal, tangent);</span>\n<span id=\"cb12-5\"><a href=\"#cb12-5\"></a>  mat3 tbn      = mat3(tangent, binormal, normal);</span>\n<span id=\"cb12-6\"><a href=\"#cb12-6\"></a></span>\n<span id=\"cb12-7\"><a href=\"#cb12-7\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the random and normal vectors, assemble the tangent, binormal, and normal matrix. You'll need this matrix to transform the sample vectors from tangent space to view space.</p>\n<div class=\"sourceCode\" id=\"cb13\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb13-1\"><a href=\"#cb13-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb13-2\"><a href=\"#cb13-2\"></a></span>\n<span id=\"cb13-3\"><a href=\"#cb13-3\"></a>  <span class=\"dt\">float</span> occlusion = NUM_SAMPLES;</span>\n<span id=\"cb13-4\"><a href=\"#cb13-4\"></a></span>\n<span id=\"cb13-5\"><a href=\"#cb13-5\"></a>  <span class=\"cf\">for</span> (<span class=\"dt\">int</span> i = <span class=\"dv\">0</span>; i &lt; NUM_SAMPLES; ++i) {</span>\n<span id=\"cb13-6\"><a href=\"#cb13-6\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb13-7\"><a href=\"#cb13-7\"></a>  }</span>\n<span id=\"cb13-8\"><a href=\"#cb13-8\"></a></span>\n<span id=\"cb13-9\"><a href=\"#cb13-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>With the matrix in hand, the shader can now loop through the samples, subtracting how many are not occluded.</p>\n<div class=\"sourceCode\" id=\"cb14\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb14-1\"><a href=\"#cb14-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb14-2\"><a href=\"#cb14-2\"></a></span>\n<span id=\"cb14-3\"><a href=\"#cb14-3\"></a>    vec3 samplePosition = tbn * samples[i];</span>\n<span id=\"cb14-4\"><a href=\"#cb14-4\"></a>         samplePosition = position.xyz + samplePosition * radius;</span>\n<span id=\"cb14-5\"><a href=\"#cb14-5\"></a></span>\n<span id=\"cb14-6\"><a href=\"#cb14-6\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the matrix, position the sample near the vertex/fragment position and scale it by the radius.</p>\n<div class=\"sourceCode\" id=\"cb15\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb15-1\"><a href=\"#cb15-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb15-2\"><a href=\"#cb15-2\"></a></span>\n<span id=\"cb15-3\"><a href=\"#cb15-3\"></a>    vec4 offsetUV      = vec4(samplePosition, <span class=\"fl\">1.0</span>);</span>\n<span id=\"cb15-4\"><a href=\"#cb15-4\"></a>         offsetUV      = lensProjection * offsetUV;</span>\n<span id=\"cb15-5\"><a href=\"#cb15-5\"></a>         offsetUV.xyz /= offsetUV.w;</span>\n<span id=\"cb15-6\"><a href=\"#cb15-6\"></a>         offsetUV.xy   = offsetUV.xy * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span>;</span>\n<span id=\"cb15-7\"><a href=\"#cb15-7\"></a></span>\n<span id=\"cb15-8\"><a href=\"#cb15-8\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the sample's position in view space, transform it from view space to clip space to UV space.</p>\n<div class=\"sourceCode\" id=\"cb16\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb16-1\"><a href=\"#cb16-1\"></a>-<span class=\"dv\">1</span> * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span> = <span class=\"dv\">0</span></span>\n<span id=\"cb16-2\"><a href=\"#cb16-2\"></a> <span class=\"dv\">1</span> * <span class=\"fl\">0.5</span> + <span class=\"fl\">0.5</span> = <span class=\"dv\">1</span></span></code></pre></div>\n<p>Recall that clip space components range from negative one to one and that UV coordinates range from zero to one. To transform clip space coordinates to UV coordinates, multiply by one half and add one half.</p>\n<div class=\"sourceCode\" id=\"cb17\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb17-1\"><a href=\"#cb17-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb17-2\"><a href=\"#cb17-2\"></a></span>\n<span id=\"cb17-3\"><a href=\"#cb17-3\"></a>    vec4 offsetPosition = texture(positionTexture, offsetUV.xy);</span>\n<span id=\"cb17-4\"><a href=\"#cb17-4\"></a></span>\n<span id=\"cb17-5\"><a href=\"#cb17-5\"></a>    <span class=\"dt\">float</span> occluded = <span class=\"dv\">0</span>;</span>\n<span id=\"cb17-6\"><a href=\"#cb17-6\"></a>    <span class=\"cf\">if</span> (samplePosition.y + bias &lt;= offsetPosition.y) { occluded = <span class=\"dv\">0</span>; } <span class=\"cf\">else</span> { occluded = <span class=\"dv\">1</span>; }</span>\n<span id=\"cb17-7\"><a href=\"#cb17-7\"></a></span>\n<span id=\"cb17-8\"><a href=\"#cb17-8\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Using the offset UV coordinates, created by projecting the 3D sample onto the 2D position texture, find the corresponding position vector. This takes you from view space to clip space to UV space back to view space. The shader takes this round trip to find out if some geometry is behind, at, or in front of this sample. If the sample is in front of or at some geometry, this sample doesn't count towards the fragment being occluded. If the sample is behind some geometry, this sample counts towards the fragment being occluded.</p>\n<div class=\"sourceCode\" id=\"cb18\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb18-1\"><a href=\"#cb18-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb18-2\"><a href=\"#cb18-2\"></a></span>\n<span id=\"cb18-3\"><a href=\"#cb18-3\"></a>    <span class=\"dt\">float</span> intensity =</span>\n<span id=\"cb18-4\"><a href=\"#cb18-4\"></a>      smoothstep</span>\n<span id=\"cb18-5\"><a href=\"#cb18-5\"></a>        ( <span class=\"fl\">0.0</span></span>\n<span id=\"cb18-6\"><a href=\"#cb18-6\"></a>        , <span class=\"fl\">1.0</span></span>\n<span id=\"cb18-7\"><a href=\"#cb18-7\"></a>        ,   radius</span>\n<span id=\"cb18-8\"><a href=\"#cb18-8\"></a>          / abs(position.y - offsetPosition.y)</span>\n<span id=\"cb18-9\"><a href=\"#cb18-9\"></a>        );</span>\n<span id=\"cb18-10\"><a href=\"#cb18-10\"></a>    occluded *= intensity;</span>\n<span id=\"cb18-11\"><a href=\"#cb18-11\"></a></span>\n<span id=\"cb18-12\"><a href=\"#cb18-12\"></a>    occlusion -= occluded;</span>\n<span id=\"cb18-13\"><a href=\"#cb18-13\"></a></span>\n<span id=\"cb18-14\"><a href=\"#cb18-14\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Now weight this sampled position by how far it is inside or outside the radius. Finally, subtract this sample from the occlusion factor since it assumes all of the samples are occluded before the loop.</p>\n<div class=\"sourceCode\" id=\"cb19\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb19-1\"><a href=\"#cb19-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb19-2\"><a href=\"#cb19-2\"></a></span>\n<span id=\"cb19-3\"><a href=\"#cb19-3\"></a>    occlusion /= NUM_SAMPLES;</span>\n<span id=\"cb19-4\"><a href=\"#cb19-4\"></a></span>\n<span id=\"cb19-5\"><a href=\"#cb19-5\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb19-6\"><a href=\"#cb19-6\"></a></span>\n<span id=\"cb19-7\"><a href=\"#cb19-7\"></a>    fragColor = vec4(vec3(occlusion), position.a);</span>\n<span id=\"cb19-8\"><a href=\"#cb19-8\"></a></span>\n<span id=\"cb19-9\"><a href=\"#cb19-9\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>Divide the occluded count by the number of samples to scale the occlusion factor from <code>[0, NUM_SAMPLES]</code> to <code>[0, 1]</code>. Zero means full occlusion and one means no occlusion. Now assign the occlusion factor to the fragment's color and you're done.</p>\n<div class=\"sourceCode\" id=\"cb20\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb20-1\"><a href=\"#cb20-1\"></a>    <span class=\"co\">// ...</span></span>\n<span id=\"cb20-2\"><a href=\"#cb20-2\"></a></span>\n<span id=\"cb20-3\"><a href=\"#cb20-3\"></a>    fragColor = vec4(vec3(occlusion), position.a);</span>\n<span id=\"cb20-4\"><a href=\"#cb20-4\"></a></span>\n<span id=\"cb20-5\"><a href=\"#cb20-5\"></a>    <span class=\"co\">// ...</span></span></code></pre></div>\n<p>For the demo's purposes, the example code sets the alpha channel to alpha channel of the position framebuffer texture to avoid covering up the background.</p>\n<h3 id=\"blurring\">Blurring</h3>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/QsqOhFR.gif\" alt=\"SSAO Blur Texture\" title=\"SSAO Blur Texture\">\n</p>\n\n<p>The SSAO framebuffer texture is noisy as is. You'll want to blur it to remove the noise. Refer back to the section on <a href=\"blur.html\">blurring</a>. For the best results, use a median or Kuwahara filter to preserve the sharp edges.</p>\n<h3 id=\"ambient-color\">Ambient Color</h3>\n<div class=\"sourceCode\" id=\"cb21\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb21-1\"><a href=\"#cb21-1\"></a>  <span class=\"co\">// ...</span></span>\n<span id=\"cb21-2\"><a href=\"#cb21-2\"></a></span>\n<span id=\"cb21-3\"><a href=\"#cb21-3\"></a>  vec2 ssaoBlurTexSize  = textureSize(ssaoBlurTexture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb21-4\"><a href=\"#cb21-4\"></a>  vec2 ssaoBlurTexCoord = gl_FragCoord.xy / ssaoBlurTexSize;</span>\n<span id=\"cb21-5\"><a href=\"#cb21-5\"></a>  <span class=\"dt\">float</span> ssao            = texture(ssaoBlurTexture, ssaoBlurTexCoord).r;</span>\n<span id=\"cb21-6\"><a href=\"#cb21-6\"></a></span>\n<span id=\"cb21-7\"><a href=\"#cb21-7\"></a>  vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex * ssao;</span>\n<span id=\"cb21-8\"><a href=\"#cb21-8\"></a></span>\n<span id=\"cb21-9\"><a href=\"#cb21-9\"></a>  <span class=\"co\">// ...</span></span></code></pre></div>\n<p>The final stop for SSAO is back in the lighting calculation. Here you see the occlusion factor being looked up in the SSAO framebuffer texture and then included in the ambient light calculation.</p>\n<h3 id=\"source\">Source</h3>\n<ul>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx\" target=\"_blank\" rel=\"noopener noreferrer\">main.cxx</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert\" target=\"_blank\" rel=\"noopener noreferrer\">basic.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert\" target=\"_blank\" rel=\"noopener noreferrer\">base.vert</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base.frag\" target=\"_blank\" rel=\"noopener noreferrer\">base.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag\" target=\"_blank\" rel=\"noopener noreferrer\">position.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag\" target=\"_blank\" rel=\"noopener noreferrer\">normal.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/ssao.frag\" target=\"_blank\" rel=\"noopener noreferrer\">ssao.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/median-filter.frag\" target=\"_blank\" rel=\"noopener noreferrer\">median-filter.frag</a></li>\n<li><a href=\"https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/kuwahara-filter.frag\" target=\"_blank\" rel=\"noopener noreferrer\">kuwahara-filter.frag</a></li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"bloom.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"motion-blur.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "docs/style.css",
    "content": "html {\n  font-size: 100%;\n  overflow-y: scroll;\n  -webkit-text-size-adjust: 100%;\n  -ms-text-size-adjust: 100%;\n}\n\nbody {\n  color: #444;\n  font-family: Helvetica, Arial, sans-serif;\n  font-size: 20px;\n  line-height: 2;\n  padding: 1em;\n  margin: auto;\n  max-width: 887px;\n  background: #fefefe;\n}\n\na {\n  color: #059;\n  text-decoration: none;\n}\n\na:visited {\n  color: #048;\n}\n\na:hover {\n  color: #06a;\n}\n\na:active {\n  color: #06a;\n}\n\na:focus {\n  outline: thin dotted;\n}\n\n*::-moz-selection {\n  background: rgba(0, 200, 255, 0.3);\n  color: #111;\n}\n\n*::selection {\n  background: rgba(0, 200, 255, 0.3);\n  color: #111;\n}\n\na::-moz-selection {\n  background: rgba(0, 200, 255, 0.3);\n  color: #048;\n}\n\na::selection {\n  background: rgba(0, 200, 255, 0.3);\n  color: #048;\n}\n\na > span.emoji {\n  font-size: 30px;\n  margin-left: 5px;\n}\n\np {\n  margin: 1em 0;\n}\n\nimg {\n  max-width: 100%;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  color: #111;\n  line-height: 125%;\n  margin-top: 1em;\n  font-weight: lighter;\n  font-family: 'Roboto Condensed', Helvetica, Arial, sans-serif;\n}\n\nh4, h5, h6 {\n  font-weight: bold;\n}\n\nh1 {\n  font-size: 2.5em;\n}\n\nh2 {\n  font-size: 2em;\n}\n\nh3 {\n  font-size: 1.5em;\n}\n\nh4 {\n  font-size: 1.2em;\n}\n\nh5 {\n  font-size: 1em;\n}\n\nh6 {\n  font-size: 0.9em;\n}\n\nblockquote {\n  color: #666;\n  text-align: justify;\n  margin-top: 60px;\n}\n\nblockquote > footer {\n  text-align: right;\n}\n\nblockquote::before {\n  content: open-quote;\n  font-size: 100px;\n  color: #999;\n  line-height: 0px;\n  padding-right: 5px;\n  vertical-align: text-bottom;\n}\n\nhr {\n  display: block;\n  height: 2px;\n  border: 0;\n  border-top: 1px solid #aaa;\n  border-bottom: 1px solid #eee;\n  margin: 1em 0;\n  padding: 0;\n}\n\npre, code, kbd, samp {\n  font-family: monospace;\n  font-size: 14px;\n}\n\npre {\n  white-space: pre;\n  white-space: pre-wrap;\n  word-wrap: break-word;\n  padding: 15px;\n}\n\nb, strong {\n  font-weight: bold;\n}\n\np > code {\n  font-weight: bold;\n  color: #444;\n  background-color: #2326291c;\n  padding: 0px 2px 0px 2px;\n}\n\ndfn {\n  font-style: italic;\n}\n\nins {\n  background: #ff9;\n  color: #000;\n  text-decoration: none;\n}\n\nmark {\n  background: #ff0;\n  color: #000;\n  font-style: italic;\n  font-weight: bold;\n}\n\nsub, sup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\nul, ol {\n  margin: 1em 0;\n  padding: 0 0 0 2em;\n}\n\nli p:last-child {\n  margin-bottom: 0;\n}\n\nul ul, ol ol {\n  margin: .3em 0;\n}\n\ndl {\n  margin-bottom: 1em;\n}\n\ndt {\n  font-weight: bold;\n  margin-bottom: .8em;\n}\n\ndd {\n  margin: 0 0 .8em 2em;\n}\n\ndd:last-child {\n  margin-bottom: 0;\n}\n\nimg {\n  border: 0;\n  -ms-interpolation-mode: bicubic;\n  vertical-align: middle;\n}\n\nfigure {\n  display: block;\n  text-align: center;\n  margin: 1em 0;\n}\n\nfigure img {\n  border: none;\n  margin: 0 auto;\n}\n\nfigcaption {\n  font-size: 0.8em;\n  font-style: italic;\n  margin: 0 0 .8em;\n}\n\ntable {\n  margin-bottom: 2em;\n  border-bottom: 1px solid #ddd;\n  border-right: 1px solid #ddd;\n  border-spacing: 0;\n  border-collapse: collapse;\n}\n\ntable th {\n  padding: .2em 1em;\n  background-color: #eee;\n  border-top: 1px solid #ddd;\n  border-left: 1px solid #ddd;\n}\n\ntable td {\n  padding: .2em 1em;\n  border-top: 1px solid #ddd;\n  border-left: 1px solid #ddd;\n  vertical-align: top;\n}\n\nkbd {\n  border: 1px solid #999;\n  padding: 5px;\n  border-radius: 2px;\n  background-color: #555;\n  color: #eee;\n  white-space: nowrap;\n}\n\n.author {\n  font-size: 1.2em;\n  text-align: center;\n}\n\n@media print {\n  * {\n    background: transparent !important;\n    color: black !important;\n    filter: none !important;\n    -ms-filter: none !important;\n  }\n\n  body {\n    font-size: 12pt;\n    max-width: 100%;\n  }\n\n  a, a:visited {\n    text-decoration: underline;\n  }\n\n  hr {\n    height: 1px;\n    border: 0;\n    border-bottom: 1px solid black;\n  }\n\n  a[href]:after {\n    content: \" (\" attr(href) \")\";\n  }\n\n  abbr[title]:after {\n    content: \" (\" attr(title) \")\";\n  }\n\n  .ir a:after, a[href^=\"javascript:\"]:after, a[href^=\"#\"]:after {\n    content: \"\";\n  }\n\n  pre, blockquote {\n    border: 1px solid #999;\n    padding-right: 1em;\n    page-break-inside: avoid;\n  }\n\n  tr, img {\n    page-break-inside: avoid;\n  }\n\n  img {\n    max-width: 100% !important;\n  }\n\n  @page :left {\n    margin: 15mm 20mm 15mm 10mm;\n}\n\n  @page :right {\n    margin: 15mm 10mm 15mm 20mm;\n}\n\n  p, h2, h3 {\n    orphans: 3;\n    widows: 3;\n  }\n\n  h2, h3 {\n    page-break-after: avoid;\n  }\n}\n"
  },
  {
    "path": "docs/texturing.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"\" xml:lang=\"\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\n    <meta name=\"description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:title\" content=\"Texturing | 3D Game Shaders For Beginners\" />\n    <meta property=\"og:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta property=\"og:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:title\" content=\"Texturing | 3D Game Shaders For Beginners\" />\n    <meta name=\"twitter:description\" content=\"Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights.\" />\n    <meta name=\"twitter:image\" content=\"https://i.imgur.com/JIDwVTm.png\" />\n    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n    <meta name=\"author\" content=\"David Lettier\" />\n    <title>Texturing | 3D Game Shaders For Beginners</title>\n    <style>\n      code{white-space: pre-wrap;}\n      span.smallcaps{font-variant: small-caps;}\n      span.underline{text-decoration: underline;}\n      div.column{display: inline-block; vertical-align: top; width: 50%;}\n    </style>\n    <style>\n      code.sourceCode > span { display: inline-block; line-height: 1.25; }\n      code.sourceCode > span { color: inherit; text-decoration: inherit; }\n      code.sourceCode > span:empty { height: 1.2em; }\n      .sourceCode { overflow: visible; }\n      code.sourceCode { white-space: pre; position: relative; }\n      div.sourceCode { margin: 1em 0; }\n      pre.sourceCode { margin: 0; }\n      @media screen {\n      div.sourceCode { overflow: auto; }\n      }\n      @media print {\n      code.sourceCode { white-space: pre-wrap; }\n      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }\n      }\n      pre.numberSource code\n        { counter-reset: source-line 0; }\n      pre.numberSource code > span\n        { position: relative; left: -4em; counter-increment: source-line; }\n      pre.numberSource code > span > a:first-child::before\n        { content: counter(source-line);\n          position: relative; left: -1em; text-align: right; vertical-align: baseline;\n          border: none; display: inline-block;\n          -webkit-touch-callout: none; -webkit-user-select: none;\n          -khtml-user-select: none; -moz-user-select: none;\n          -ms-user-select: none; user-select: none;\n          padding: 0 4px; width: 4em;\n          background-color: #232629;\n          color: #7a7c7d;\n        }\n      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }\n      div.sourceCode\n        { color: #cfcfc2; background-color: #232629; }\n      @media screen {\n      code.sourceCode > span > a:first-child::before { text-decoration: underline; }\n      }\n      code span. { color: #cfcfc2; } /* Normal */\n      code span.al { color: #95da4c; } /* Alert */\n      code span.an { color: #3f8058; } /* Annotation */\n      code span.at { color: #2980b9; } /* Attribute */\n      code span.bn { color: #f67400; } /* BaseN */\n      code span.bu { color: #7f8c8d; } /* BuiltIn */\n      code span.cf { color: #fdbc4b; } /* ControlFlow */\n      code span.ch { color: #3daee9; } /* Char */\n      code span.cn { color: #27aeae; } /* Constant */\n      code span.co { color: #7a7c7d; } /* Comment */\n      code span.cv { color: #7f8c8d; } /* CommentVar */\n      code span.do { color: #a43340; } /* Documentation */\n      code span.dt { color: #2980b9; } /* DataType */\n      code span.dv { color: #f67400; } /* DecVal */\n      code span.er { color: #da4453; } /* Error */\n      code span.ex { color: #0099ff; } /* Extension */\n      code span.fl { color: #f67400; } /* Float */\n      code span.fu { color: #8e44ad; } /* Function */\n      code span.im { color: #27ae60; } /* Import */\n      code span.in { color: #c45b00; } /* Information */\n      code span.kw { color: #cfcfc2; } /* Keyword */\n      code span.op { color: #cfcfc2; } /* Operator */\n      code span.ot { color: #27ae60; } /* Other */\n      code span.pp { color: #27ae60; } /* Preprocessor */\n      code span.re { color: #2980b9; } /* RegionMarker */\n      code span.sc { color: #3daee9; } /* SpecialChar */\n      code span.ss { color: #da4453; } /* SpecialString */\n      code span.st { color: #f44f4f; } /* String */\n      code span.va { color: #27aeae; } /* Variable */\n      code span.vs { color: #da4453; } /* VerbatimString */\n      code span.wa { color: #da4453; } /* Warning */\n    </style>\n    <!--[if lt IE 9]>\n      <script src=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js\"></script>\n    <![endif]-->\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n<p><a href=\"render-to-texture.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n<h1 id=\"3d-game-shaders-for-beginners\">3D Game Shaders For Beginners</h1>\n<h2 id=\"texturing\">Texturing</h2>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/cqbgT8b.gif\" alt=\"Diffuse Texture Only\" title=\"Diffuse Texture Only\">\n</p>\n\n<p>Texturing involves mapping some color or some other kind of vector to a fragment using UV coordinates. Both U and V range from zero to one. Each vertex gets a UV coordinate and this is outputted in the vertex shader.</p>\n<p align=\"center\">\n<img src=\"https://i.imgur.com/JjAdNfk.png\" alt=\"UV Interpolation\" title=\"UV Interpolation\">\n</p>\n\n<p>The fragment shader receives the UV coordinate interpolated. Interpolated meaning the UV coordinate for the fragment is somewhere between the UV coordinates for the vertexes that make up the triangle face.</p>\n<h3 id=\"vertex\">Vertex</h3>\n<div class=\"sourceCode\" id=\"cb1\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb1-1\"><a href=\"#cb1-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb1-2\"><a href=\"#cb1-2\"></a></span>\n<span id=\"cb1-3\"><a href=\"#cb1-3\"></a>uniform mat4 p3d_ModelViewProjectionMatrix;</span>\n<span id=\"cb1-4\"><a href=\"#cb1-4\"></a></span>\n<span id=\"cb1-5\"><a href=\"#cb1-5\"></a>in vec2 p3d_MultiTexCoord0;</span>\n<span id=\"cb1-6\"><a href=\"#cb1-6\"></a></span>\n<span id=\"cb1-7\"><a href=\"#cb1-7\"></a>in vec4 p3d_Vertex;</span>\n<span id=\"cb1-8\"><a href=\"#cb1-8\"></a></span>\n<span id=\"cb1-9\"><a href=\"#cb1-9\"></a>out vec2 texCoord;</span>\n<span id=\"cb1-10\"><a href=\"#cb1-10\"></a></span>\n<span id=\"cb1-11\"><a href=\"#cb1-11\"></a><span class=\"dt\">void</span> main()</span>\n<span id=\"cb1-12\"><a href=\"#cb1-12\"></a>{</span>\n<span id=\"cb1-13\"><a href=\"#cb1-13\"></a>  texCoord = p3d_MultiTexCoord0;</span>\n<span id=\"cb1-14\"><a href=\"#cb1-14\"></a></span>\n<span id=\"cb1-15\"><a href=\"#cb1-15\"></a>  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;</span>\n<span id=\"cb1-16\"><a href=\"#cb1-16\"></a>}</span></code></pre></div>\n<p>Here you see the vertex shader outputting the texture coordinate to the fragment shader. Notice how it's a two dimensional vector. One dimension for U and one for V.</p>\n<h3 id=\"fragment\">Fragment</h3>\n<div class=\"sourceCode\" id=\"cb2\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb2-1\"><a href=\"#cb2-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb2-2\"><a href=\"#cb2-2\"></a></span>\n<span id=\"cb2-3\"><a href=\"#cb2-3\"></a>uniform sampler2D p3d_Texture0;</span>\n<span id=\"cb2-4\"><a href=\"#cb2-4\"></a></span>\n<span id=\"cb2-5\"><a href=\"#cb2-5\"></a>in vec2 texCoord;</span>\n<span id=\"cb2-6\"><a href=\"#cb2-6\"></a></span>\n<span id=\"cb2-7\"><a href=\"#cb2-7\"></a>out vec2 fragColor;</span>\n<span id=\"cb2-8\"><a href=\"#cb2-8\"></a></span>\n<span id=\"cb2-9\"><a href=\"#cb2-9\"></a><span class=\"dt\">void</span> main()</span>\n<span id=\"cb2-10\"><a href=\"#cb2-10\"></a>{</span>\n<span id=\"cb2-11\"><a href=\"#cb2-11\"></a>  texColor = texture(p3d_Texture0, texCoord);</span>\n<span id=\"cb2-12\"><a href=\"#cb2-12\"></a></span>\n<span id=\"cb2-13\"><a href=\"#cb2-13\"></a>  fragColor = texColor;</span>\n<span id=\"cb2-14\"><a href=\"#cb2-14\"></a>}</span></code></pre></div>\n<p>Here you see the fragment shader looking up the color at its UV coordinate and outputting that as the fragment color.</p>\n<h4 id=\"screen-filled-texture\">Screen Filled Texture</h4>\n<div class=\"sourceCode\" id=\"cb3\"><pre class=\"sourceCode c\"><code class=\"sourceCode c\"><span id=\"cb3-1\"><a href=\"#cb3-1\"></a><span class=\"er\">#version 150</span></span>\n<span id=\"cb3-2\"><a href=\"#cb3-2\"></a></span>\n<span id=\"cb3-3\"><a href=\"#cb3-3\"></a>uniform sampler2D screenSizedTexture;</span>\n<span id=\"cb3-4\"><a href=\"#cb3-4\"></a></span>\n<span id=\"cb3-5\"><a href=\"#cb3-5\"></a>out vec2 fragColor;</span>\n<span id=\"cb3-6\"><a href=\"#cb3-6\"></a></span>\n<span id=\"cb3-7\"><a href=\"#cb3-7\"></a><span class=\"dt\">void</span> main()</span>\n<span id=\"cb3-8\"><a href=\"#cb3-8\"></a>{</span>\n<span id=\"cb3-9\"><a href=\"#cb3-9\"></a>  vec2 texSize  = textureSize(texture, <span class=\"dv\">0</span>).xy;</span>\n<span id=\"cb3-10\"><a href=\"#cb3-10\"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>\n<span id=\"cb3-11\"><a href=\"#cb3-11\"></a></span>\n<span id=\"cb3-12\"><a href=\"#cb3-12\"></a>  texColor = texture(screenSizedTexture, texCoord);</span>\n<span id=\"cb3-13\"><a href=\"#cb3-13\"></a></span>\n<span id=\"cb3-14\"><a href=\"#cb3-14\"></a>  fragColor = texColor;</span>\n<span id=\"cb3-15\"><a href=\"#cb3-15\"></a>}</span></code></pre></div>\n<p>When performing render to texture, the mesh is a flat rectangle with the same aspect ratio as the screen. Because of this, you can calculate the UV coordinates knowing only A) the width and height of the screen sized texture being UV mapped to the rectangle and B) the fragment's x and y coordinate. To map x to U, divide x by the width of the input texture. Similarly, to map y to V, divide y by the height of the input texture. You'll see this technique used in the example code.</p>\n<h2 id=\"copyright\">Copyright</h2>\n<p>(C) 2019 David Lettier <br> <a href=\"https://www.lettier.com\">lettier.com</a></p>\n<p><a href=\"render-to-texture.html\"><span class=\"emoji\" data-emoji=\"arrow_backward\">◀️</span></a> <a href=\"index.html\"><span class=\"emoji\" data-emoji=\"arrow_double_up\">⏫</span></a> <a href=\"#\"><span class=\"emoji\" data-emoji=\"arrow_up_small\">🔼</span></a> <a href=\"#copyright\"><span class=\"emoji\" data-emoji=\"arrow_down_small\">🔽</span></a> <a href=\"lighting.html\"><span class=\"emoji\" data-emoji=\"arrow_forward\">▶️</span></a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "sections/blinn-phong.md",
    "content": "[:arrow_backward:](lighting.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](fresnel-factor.md)\n\n# 3D Game Shaders For Beginners\n\n## Blinn-Phong\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/CFWEeGK.gif\" alt=\"Blinn-Phong\" title=\"Blinn-Phong\">\n</p>\n\nBlinn-Phong is a slight adjustment of the Phong model you saw in the [lighting](lighting.md) section.\nIt provides more plausible or realistic specular reflections.\nYou'll notice that Blinn-Phong produces elliptical or elongated specular reflections\nversus the spherical specular reflections produced by the Phong model.\nIn certain cases, Blinn-Phong can be more efficient to calculate than Phong.\n\n```c\n  // ...\n\n  vec3 light   = normal(lightPosition.xyz - vertexPosition.xyz);\n  vec3 eye     = normalize(-vertexPosition.xyz);\n  vec3 halfway = normalize(light + eye);\n\n  // ...\n```\n\nInstead of computing the reflection vector,\ncompute the halfway or half angle vector.\nThis vector is between the view/camera/eye and light direction vector.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/vtqd1Ox.gif\" alt=\"Blinn-Phong vs Phong\" title=\"Blinn-Phong vs Phong\">\n</p>\n\n```c\n    // ...\n\n    float specularIntensity = dot(normal, halfway);\n\n    // ...\n```\n\nThe specular intensity is now the dot product of the normal and halfway vector.\nIn the Phong model, it is the dot product of the reflection and view vector.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WZQqxEH.png\" alt=\"Full specular intensity.\" title=\"Full specular intensity.\">\n</p>\n\nThe half angle vector (magenta arrow) will point in the same direction as the normal (green arrow) when the\nview vector (orange arrow) points in the same direction as the reflection vector (magenta arrow).\nIn this case, both the Blinn-Phong and Phong specular intensity will be one.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/kiSdJzt.png\" alt=\"Blinn-Phong vs Phong\" title=\"Blinn-Phong vs Phong\">\n</p>\n\nIn other cases, the specular intensity for Blinn-Phong will be greater than zero\nwhile the specular intensity for Phong will be zero.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](lighting.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](fresnel-factor.md)\n"
  },
  {
    "path": "sections/bloom.md",
    "content": "[:arrow_backward:](blur.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](ssao.md)\n\n# 3D Game Shaders For Beginners\n\n## Bloom\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/UxKRz2r.gif\" alt=\"Bloom\" title=\"Bloom\">\n</p>\n\nAdding bloom to a scene can really sell the illusion of the lighting model.\nLight emitting objects are more believable and specular highlights get an extra dose of shimmer.\n\n```c\n  //...\n\n  int   size       = 5;\n  float separation = 3;\n  float threshold  = 0.4;\n  float amount     = 1;\n\n  // ...\n```\n\nThese parameters control the look and feel.\n`size` determines how blurred the effect is.\n`separation` spreads out the blur.\n`threshold` controls which fragments are illuminated.\nAnd the last parameter, `amount`, controls how much bloom is outputted.\n\n```c\n  // ...\n\n  vec2 texSize = textureSize(colorTexture, 0).xy;\n\n  float value = 0.0;\n  float count = 0.0;\n\n  vec4 result = vec4(0);\n  vec4 color  = vec4(0);\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      // ...\n    }\n  }\n\n  // ...\n```\n\nThe technique starts by looping through a kernel/matrix/window centered over the current fragment.\nThis is similar to the window used for [outlining](outlining.md).\nThe size of the window is `size * 2 + 1` by `size * 2 + 1`.\nSo for example, with a `size` setting of two, the window uses `(2 * 2 + 1)^2 = 25` samples per fragment.\n\n```c\n      // ...\n\n      color =\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + (vec2(i, j) * separation)\n              )\n            / texSize\n          );\n\n      value = max(color.r, max(color.g, color.b));\n      if (value < threshold) { color = vec4(0); }\n\n      result += color;\n      count  += 1.0;\n\n      // ...\n```\n\nFor each iteration,\nit retrieves the color from the input texture and turns the red, green, and blue values into a greyscale value.\nIf this greyscale value is less than the threshold, it discards this color by making it solid black.\nAfter evaluating the sample's greyscale value, it adds its RGB values to `result`.\n\n```c\n  // ...\n\n  result /= count;\n\n  fragColor = mix(vec4(0), result, amount);\n\n  // ...\n```\n\nAfter it's done summing up the samples, it divides the sum of the color samples by the number of samples taken.\nThe result is the average color of itself and its neighbors.\nBy doing this for every fragment, you end up with a blurred image.\nThis form of blurring is known as a [box blur](blur.md#box-blur).\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/m4yedrM.gif\" alt=\"Bloom progresssion.\" title=\"Bloom progresssion.\">\n</p>\n\nHere you see the progression of the bloom algorithm.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [bloom.frag](../demonstration/shaders/fragment/outline.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](blur.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](ssao.md)\n"
  },
  {
    "path": "sections/blur.md",
    "content": "[:arrow_backward:](fog.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](bloom.md)\n\n# 3D Game Shaders For Beginners\n\n## Blur\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b5vw2AJ.gif\" alt=\"Kuwahara Filter\" title=\"Kuwahara Filter\">\n</p>\n\nThe need to blur this or that can come up quite often as you try to obtain\na particular look or perform some technique like motion blur.\nBelow are just some of ways you can blur your game's imagery.\n\n### Box Blur\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uaXC1JM.gif\" alt=\"Box Blur\" title=\"Box Blur\">\n</p>\n\nThe box blur or mean filter algorithm is a simple to implement blurring effect.\nIt's fast and gets the job done.\nIf you need more finesse, you can upgrade to a Gaussian blur.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  int size  = int(parameters.x);\n  if (size <= 0) { fragColor = texture(colorTexture, texCoord); return; }\n\n  // ...\n```\n\nThe `size` parameter controls how blurry the result is.\nIf the `size` is zero or less, return the fragment untouched.\n\n```c\n  // ...\n\n  float separation = parameters.y;\n        separation = max(separation, 1);\n\n  // ...\n```\n\nThe `separation` parameter spreads out the blur without having to sample additional fragments.\n`separation` ranges from one to infinity.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bRw0OkX.png\" alt=\"Blur Kernel\" title=\"Blur Kernel\">\n</p>\n\n```c\n  // ...\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      // ...\n    }\n  }\n\n  // ...\n```\n\nLike the [outlining](outlining.md) technique,\nthe box blur technique uses a kernel/matrix/window centered around the current fragment.\nThe size of the window is `size * 2 + 1` by `size * 2 + 1`.\nSo for example, with a `size` setting of two, the window uses `(2 * 2 + 1)^2 = 25` samples per fragment.\n\n```c\n      // ...\n\n      fragColor +=\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + (vec2(i, j) * separation)\n              )\n            / texSize\n          );\n\n      // ...\n```\n\nTo compute the mean or average of the samples in the window,\nstart by loop through the window, adding up each color vector.\n\n\n```c\n  // ...\n\n  fragColor /= pow(size * 2 + 1, 2);\n\n  // ...\n```\n\nTo finish computing the mean, divide the sum of the colors sampled by the number of samples taken.\nThe final fragment color is the mean or average of the fragments sampled inside the window.\n\n### Median Filter\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/T1nEEn3.gif\" alt=\"Median Filter\" title=\"Median Filter\">\n</p>\n\nThe box blur uses the mean color of the samples taken.\nThe median filter uses the median color of the samples taken.\nBy using the median instead of the mean,\nthe edges in the image are preserved—meaning the edges stay nice and crisp.\nFor example, look at the windows in the box blurred image versus the median filtered image.\n\nUnfortunately, finding the median can be slower than finding the mean.\nYou could sort the values and choose the middle one but that would take at least quasilinear time.\nThere is a technique to find the median in linear time but it can be quite awkward inside a shader.\nThe numerical approach below approximates the median in linear time.\nHow well it approximates the median can be controlled.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AnbzUmN.png\" alt=\"Painterly\" title=\"Painterly\">\n</p>\n\nAt lower quality approximations,\nyou end up with a nice [painterly](https://en.wikipedia.org/wiki/Painterliness) look.\n\n```c\n// ...\n\n#define MAX_SIZE        4\n#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))\n#define MAX_BINS_SIZE   100\n\n// ...\n```\n\nThese are the hard limits for the `size` parameter, window size, and `bins` array.\n\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  int size = int(parameters.x);\n  if (size <= 0) { fragColor = texture(colorTexture, texCoord); return; }\n  if (size > MAX_SIZE) { size = MAX_SIZE; }\n  int kernelSize = int(pow(size * 2 + 1, 2));\n\n  // ...\n```\n\nThe `size` parameter controls how blurry or smeared the effect is.\nIf the size is at or below zero, return the current fragment untouched.\nFrom the `size` parameter, calculate the total size of the kernel or window.\nThis is how many samples you'll be taking per fragment.\n\n```c\n  // ...\n\n  int binsSize = int(parameters.y);\n      binsSize = clamp(binsSize, 1, MAX_BINS_SIZE);\n\n  // ...\n```\n\nSet up the `binsSize`, making sure to limit it by the `MAX_BINS_SIZE`.\n\n```c\n  // ...\n\n  int i        = 0;\n  int j        = 0;\n  int count    = 0;\n  int binIndex = 0;\n\n  // ...\n```\n\n`i` and `j` are used to sample the given texture around the current fragment.\n`i` is also used as a general for loop count.\n`count` is used in the initialization of the `colors` array which you'll see later.\n`binIndex` is used to approximate the median color.\n\n```c\n  // ...\n\n  vec4  colors[MAX_KERNEL_SIZE];\n  float bins[MAX_BINS_SIZE];\n  int   binIndexes[colors.length()];\n\n  // ...\n```\n\nThe `colors` array holds the sampled colors taken from the input texture.\n`bins` is used to approximate the median of the sampled colors.\nEach bin holds a count of how many colors fall into its range when converting each color into a greyscale value (between zero and one).\nAs `binsSize` approaches 100, the algorithm finds the true median almost always.\n`binIndexes` stores the `bins` index or which bin each sample falls into.\n\n```c\n  // ...\n\n  float total = 0;\n  float limit = floor(float(kernelSize) / 2) + 1;\n\n  // ...\n```\n\n`total` keeps track of how many colors you've come across as you loop through `bins`.\nWhen `total` reaches `limit`, you return whatever `bins` index you're at.\nThe `limit` is the median index.\nFor example, if the window size is 81, `limit` is 41 which is directly in the middle (40 samples below and 40 samples above).\n\n```c\n  // ...\n\n  float value       = 0;\n  vec3  valueRatios = vec3(0.3, 0.59, 0.11);\n\n  // ...\n```\n\nThese are used to covert and hold each color sample's greyscale value.\nInstead of dividing red, green, and blue by one third,\nit uses 30% of red, 59% of green, and 11% of blue for a total of 100%.\n\n```c\n  // ...\n\n  for (i = -size; i <= size; ++i) {\n    for (j = -size; j <= size; ++j) {\n      colors[count] =\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + vec2(i, j)\n              )\n            / texSize\n          );\n      count += 1;\n    }\n  }\n\n  // ...\n```\n\nLoop through the window and collect the color samples into `colors`.\n\n```c\n  // ...\n\n  for (i = 0; i < binsSize; ++i) {\n    bins[i] = 0;\n  }\n\n  // ...\n```\n\nInitialize the `bins` array with zeros.\n\n```c\n  // ...\n\n  for (i = 0; i < kernelSize; ++i) {\n    value           = dot(colors[i].rgb, valueRatios);\n    binIndex        = int(floor(value * binsSize));\n    binIndex        = clamp(binIndex, 0, binsSize - 1);\n    bins[binIndex] += 1;\n    binIndexes[i]   = binIndex;\n  }\n\n  // ...\n```\n\nLoop through the colors and convert each one to a greyscale value.\n`dot(colors[i].rgb, valueRatios)` is the weighted sum `colors.r * 0.3 + colors.g * 0.59 + colors.b * 0.11`.\n\nEach value will fall into some bin.\nEach bin covers some range of values.\nFor example, if the number of bins is 10, the first bin covers everything from zero up to but not including 0.1.\nIncrement the number of colors that fall into this bin and remember the color sample's bin index so you can look it up later.\n\n```c\n  // ...\n\n  binIndex = 0;\n\n  for (i = 0; i < binsSize; ++i) {\n    total += bins[i];\n    if (total >= limit) {\n      binIndex = i;\n      break;\n    }\n  }\n\n  // ...\n```\n\nLoop through the bins, tallying up the number of colors seen so far.\nWhen you reach the median index, exit the loop and remember the last `bins` index reached.\n\n```c\n  // ...\n\n  fragColor = colors[0];\n\n  for (i = 0; i < kernelSize; ++i) {\n    if (binIndexes[i] == binIndex) {\n      fragColor = colors[i];\n      break;\n    }\n  }\n\n  // ...\n```\n\nNow loop through the `binIndexes` and find the first color with the last `bins` indexed reached.\nIts greyscale value is the approximated median which in many cases will be the true median value.\nSet this color as the fragColor and exit the loop and shader.\n\n### Kuwahara Filter\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b5vw2AJ.gif\" alt=\"Kuwahara Filter\" title=\"Kuwahara Filter\">\n</p>\n\nLike the median filter, the kuwahara filter preserves the major edges found in the image.\nYou'll notice that it has a more block like or chunky pattern to it.\nIn practice,\nthe Kuwahara filter runs faster than the median filter, allowing for larger `size` values without a noticeable slowdown.\n\n```c\n// ...\n\n#define MAX_SIZE        5\n#define MAX_KERNEL_SIZE ((MAX_SIZE * 2 + 1) * (MAX_SIZE * 2 + 1))\n\n// ...\n```\n\nSet a hard limit for the `size` parameter and the number of samples taken.\n\n```c\n// ...\n\nint i     = 0;\nint j     = 0;\nint count = 0;\n\n// ...\n```\n\nThese are used to sample the input texture and set up the `values` array.\n\n```c\n// ...\n\nvec3  valueRatios = vec3(0.3, 0.59, 0.11);\n\n// ...\n```\n\nLike the median filter, you'll be converting the color samples into greyscale values.\n\n```c\n// ...\n\nfloat values[MAX_KERNEL_SIZE];\n\n// ...\n```\n\nInitialize the `values` array.\nThis will hold the greyscale values for the color samples.\n\n```c\n// ...\n\nvec4  color       = vec4(0);\nvec4  meanTemp    = vec4(0);\nvec4  mean        = vec4(0);\nfloat valueMean   = 0;\nfloat variance    = 0;\nfloat minVariance = -1;\n\n// ...\n```\n\nThe Kuwahara filter works by computing the variance of four subwindows and then using the mean of the subwindow with the smallest variance.\n\n```c\n// ...\n\nvoid findMean(int i0, int i1, int j0, int j1) {\n\n// ...\n```\n\n`findMean` is a function defined outside of `main`.\nEach run of `findMean` will remember the mean of the given subwindow that has the lowest variance seen so far.\n\n```c\n  // ...\n\n  meanTemp = vec4(0);\n  count    = 0;\n\n  // ...\n```\n\nMake sure to reset `count` and `meanTemp` before computing the mean of the given subwindow.\n\n```c\n  // ...\n\n  for (i = i0; i <= i1; ++i) {\n    for (j = j0; j <= j1; ++j) {\n      color  =\n        texture\n          ( colorTexture\n          ,   (gl_FragCoord.xy + vec2(i, j))\n            / texSize\n          );\n\n      meanTemp += color;\n\n      values[count] = dot(color.rgb, valueRatios);\n\n      count += 1;\n    }\n  }\n\n  // ...\n```\n\nSimilar to the box blur, loop through the given subwindow and add up each color.\nAt the same time, make sure to store the greyscale value for this sample in `values`.\n\n```c\n  // ...\n\n  meanTemp.rgb /= count;\n  valueMean     = dot(meanTemp.rgb, valueRatios);\n\n  // ...\n```\n\nTo compute the mean, divide the samples sum by the number of samples taken.\nCalculate the greyscale value for the mean.\n\n```c\n  // ...\n\n  for (i = 0; i < count; ++i) {\n    variance += pow(values[i] - valueMean, 2);\n  }\n\n  variance /= count;\n\n  // ...\n```\n\nNow calculate the variance for this given subwindow.\nThe variance is the average squared difference between each sample's greyscale value the mean greyscale value.\n\n```c\n  // ...\n\n  if (variance < minVariance || minVariance <= -1) {\n    mean = meanTemp;\n    minVariance = variance;\n  }\n}\n\n// ...\n```\n\nIf the variance is smaller than what you've seen before or this is the first variance you've seen,\nset the mean of this subwindow as the final mean and update the minimum variance seen so far.\n\n```c\n// ...\n\nvoid main() {\n  int size = int(parameters.x);\n  if (size <= 0) { fragColor = texture(colorTexture, texCoord); return; }\n\n  // ...\n```\n\nBack in `main`, set the `size` parameter.\nIf the size is at or below zero, return the fragment unchanged.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuLbLKO.gif\" alt=\"Kuwahara Kernal\" title=\"Kuwahara Kernal\">\n</p>\n\n```c\n  // ...\n\n  // Lower Left\n\n  findMean(-size, 0, -size, 0);\n\n  // Upper Right\n\n  findMean(0, size, 0, size);\n\n  // Upper Left\n\n  findMean(-size, 0, 0, size);\n\n  // Lower Right\n\n  findMean(0, size, -size, 0);\n\n  // ...\n```\n\nAs stated above,\nthe Kuwahara filter works by computing the variance of four subwindows\nand then using the mean of the subwindow with the lowest variance as the final fragment color.\nNote that the four subwindows overlap each other.\n\n```c\n  // ...\n\n  mean.a    = 1;\n  fragColor = mean;\n\n  // ...\n```\n\nAfter computing the variance and mean for each subwindow,\nset the fragment color to the mean of the subwindow with the lowest variance.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [box-blur.frag](../demonstration/shaders/fragment/box-blur.frag)\n- [median-filter.frag](../demonstration/shaders/fragment/median-filter.frag)\n- [kuwahara-filter.frag](../demonstration/shaders/fragment/kuwahara-filter.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](fog.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](bloom.md)\n"
  },
  {
    "path": "sections/building-the-demo.md",
    "content": "[:arrow_backward:](setup.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](running-the-demo.md)\n\n# 3D Game Shaders For Beginners\n\n## Building The Demo\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/PQcDnIu.gif\" alt=\"Building The Demo\" title=\"Building The Demo\">\n</p>\n\nBefore you can try out the demo program, you'll have to build the example code first.\n\n### Dependencies\n\nBefore you can compile the example code, you'll need to install\n[Panda3D](https://www.panda3d.org/)\nfor your platform.\nPanda3D is available for Linux, Mac, and Windows.\n\n### Linux\n\nStart by [installing](https://www.panda3d.org/manual/?title=Installing_Panda3D_in_Linux) the\n[Panda3D SDK](https://www.panda3d.org/download/sdk-1-10-9/) for your distribution.\n\nMake sure to locate where the Panda3D headers and libraries are.\nThe headers and libraries are most likely in `/usr/include/panda3d/` and `/usr/lib/panda3d/` respectively.\n\nNext clone this repository and change directory into it.\n\n```bash\ngit clone https://github.com/lettier/3d-game-shaders-for-beginners.git\ncd 3d-game-shaders-for-beginners/demonstration\n```\n\nNow compile the source code into an object file.\n\n```bash\ng++ \\\n  -c src/main.cxx \\\n  -o 3d-game-shaders-for-beginners.o \\\n  -std=gnu++11 \\\n  -O2 \\\n  -I/path/to/python/include/ \\\n  -I/path/to/panda3d/include/\n```\n\nWith the object file created, create the executable by linking the object file to its dependencies.\n\n```bash\ng++ \\\n  3d-game-shaders-for-beginners.o \\\n  -o 3d-game-shaders-for-beginners \\\n  -L/path/to/panda3d/lib \\\n  -lp3framework \\\n  -lpanda \\\n  -lpandafx \\\n  -lpandaexpress \\\n  -lpandaphysics \\\n  -lp3dtoolconfig \\\n  -lp3dtool \\\n  -lpthread\n```\n\nFor more help, see the [Panda3D manual](https://www.panda3d.org/manual/?title=How_to_compile_a_C++_Panda3D_program_on_Linux).\n\n### Mac\n\nStart by installing the [Panda3D SDK](https://www.panda3d.org/download/sdk-1-10-9/) for Mac.\n\nMake sure to locate where the Panda3D headers and libraries are.\n\nNext clone this repository and change directory into it.\n\n```bash\ngit clone https://github.com/lettier/3d-game-shaders-for-beginners.git\ncd 3d-game-shaders-for-beginners\n```\n\nNow compile the source code into an object file.\nYou'll have to find where the Python 2.7 and Panda3D include directories are.\n\n```bash\nclang++ \\\n  -c main.cxx \\\n  -o 3d-game-shaders-for-beginners.o \\\n  -std=gnu++11 \\\n  -g \\\n  -O2 \\\n  -I/path/to/python/include/ \\\n  -I/path/to/panda3d/include/\n```\n\nWith the object file created, create the executable by linking the object file to its dependencies.\nYou'll need to track down where the Panda3D libraries are located.\n\n```bash\nclang++ \\\n  3d-game-shaders-for-beginners.o \\\n  -o 3d-game-shaders-for-beginners \\\n  -L/path/to/panda3d/lib \\\n  -lp3framework \\\n  -lpanda \\\n  -lpandafx \\\n  -lpandaexpress \\\n  -lpandaphysics \\\n  -lp3dtoolconfig \\\n  -lp3dtool \\\n  -lpthread\n```\n\nFor more help, see the [Panda3D manual](https://www.panda3d.org/manual/?title=How_to_compile_a_C++_Panda3D_program_on_macOS).\n\n### Windows\n\nStart by [installing](https://www.panda3d.org/manual/?title=Installing_Panda3D_in_Windows) the\n[Panda3D SDK](https://www.panda3d.org/download/sdk-1-10-9/) for Windows.\n\nMake sure to locate where the Panda3D headers and libraries are.\n\nNext clone this repository and change directory into it.\n\n```bash\ngit clone https://github.com/lettier/3d-game-shaders-for-beginners.git\ncd 3d-game-shaders-for-beginners\n```\n\nFor more help, see the [Panda3D manual](https://www.panda3d.org/manual/?title=Running_your_Program&language=cxx).\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](setup.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](running-the-demo.md)\n"
  },
  {
    "path": "sections/cel-shading.md",
    "content": "[:arrow_backward:](rim-lighting.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](normal-mapping.md)\n\n# 3D Game Shaders For Beginners\n\n## Cel Shading\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/W80Ke1y.gif\" alt=\"Cel Shaded\" title=\"Cel Shaded\">\n</p>\n\nCel shading is a technique to make 3D objects look 2D or flat.\nIn 2D,\nyou can make an object look 3D by applying a smooth gradient.\nHowever, with cel shading, you're breaking up the gradients into abrupt transitions.\nTypically there is only one transition where the shading goes from fully lit to fully shadowed.\nWhen combined with [outlining](outlining.md), cel shading can really sell the 2D cartoon look.\n\n## Diffuse\n\n```c\n    // ...\n\n    float diffuseIntensity = max(dot(normal, unitLightDirection), 0.0);\n          diffuseIntensity = step(0.1, diffuseIntensity);\n\n    // ...\n```\n\nRevisiting the [lighting](lighting.md#diffuse) model,\nmodify the `diffuseIntensity` such that it is either zero or one.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/lyLweFc.png\" alt=\"Step Function\" title=\"Step Function\">\n</p>\n\nThe `step` function returns zero if the input is less than the edge and one otherwise.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EI6QJ60.png\" alt=\"Steps Function\" title=\"Steps Function\">\n</p>\n\n```c\n  // ...\n\n  if      (diffuseIntensity >= 0.8) { diffuseIntensity = 1.0; }\n  else if (diffuseIntensity >= 0.6) { diffuseIntensity = 0.6; }\n  else if (diffuseIntensity >= 0.3) { diffuseIntensity = 0.3; }\n  else                              { diffuseIntensity = 0.0; }\n\n  // ...\n```\n\nIf you would like to have a few steps or transitions,\nyou can perform something like the above.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7KK65mi.png\" alt=\"Step Texture\" title=\"Step Texture\">\n</p>\n\n```c\n  // ...\n\n  diffuseIntensity = texture(steps, vec2(diffuseIntensity, 0.0)).r;\n\n  // ...\n```\n\nAnother approach is to put your step values into a texture with the transitions going from darker to lighter.\nUsing the `diffuseIntensity` as a U coordinate, it will automatically transform itself.\n\n## Specular\n\n```c\n\n      // ...\n\n      float specularIntensity = clamp(dot(normal, halfwayDirection), 0.0, 1.0);\n            specularIntensity = step(0.98, specularIntensity);\n\n      // ...\n```\n\nUsing the `step` function again, set the `specularIntensity` to be either zero or one.\nYou can also use one of the other approaches described up above for the specular highlight as well.\nAfter you've altered the `specularIntensity`, the rest of the lighting calculations are the same.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](rim-lighting.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](normal-mapping.md)\n"
  },
  {
    "path": "sections/chromatic-aberration.md",
    "content": "[:arrow_backward:](motion-blur.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](screen-space-reflection.md)\n\n# 3D Game Shaders For Beginners\n\n## Chromatic Aberration\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bawgERm.gif\" alt=\"Chromatic Aberration\" title=\"Chromatic Aberration\">\n</p>\n\nChromatic aberration is a screen space technique that simulates lens distortion.\nUse it to give your scene a cinematic, lo-fi analog feel or to emphasize a chaotic event.\n\n### Texture\n\n```c\nuniform sampler2D colorTexture;\n\n// ...\n```\n\nThe input texture needed is the scene's colors captured into a framebuffer texture.\n\n### Parameters\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fNpMaPL.gif\" alt=\"Chromatic Aberration\" title=\"Chromatic Aberration\">\n</p>\n\n```c\n  // ...\n\n  float redOffset   =  0.009;\n  float greenOffset =  0.006;\n  float blueOffset  = -0.006;\n\n  // ...\n```\n\nThe adjustable parameters for this technique are the red, green, and blue offsets.\nFeel free to play around with these to get the particular color fringe you're looking for.\nThese particular offsets produce a yellowish orange and blue fringe.\n\n### Direction\n\n```c\n// ...\n\nuniform vec2 mouseFocusPoint;\n\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec2 direction = texCoord - mouseFocusPoint;\n\n  // ...\n```\n\nThe offsets can occur horizontally, vertically, or radially.\nOne approach is to radiate out from the [depth of field](depth-of-field.md) focal point.\nAs the scene gets more out of focus, the chromatic aberration increases.\n\n### Samples\n\n```c\n// ...\n\nout vec4 fragColor;\n\n  // ...\n\n  fragColor.r  = texture(colorTexture, texCoord + (direction * vec2(redOffset  ))).r;\n  fragColor.g  = texture(colorTexture, texCoord + (direction * vec2(greenOffset))).g;\n  fragColor.ba = texture(colorTexture, texCoord + (direction * vec2(blueOffset ))).ba;\n}\n```\n\nWith the direction and offsets,\nmake three samples of the scene's colors—one for the red, green, and blue channels.\nThese will be the final fragment color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [chromatic-aberration.frag](../demonstration/shaders/fragment/chromatic-aberration.frag)\n\n## Copyright\n\n(C) 2021 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](motion-blur.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](screen-space-reflection.md)\n"
  },
  {
    "path": "sections/deferred-rendering.md",
    "content": "[:arrow_backward:](normal-mapping.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](fog.md)\n\n# 3D Game Shaders For Beginners\n\n## Deferred Rendering\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/rxTYLGv.png\" alt=\"Deferred vs Forward\" title=\"Deferred vs Forward\">\n</p>\n\nDeferred rendering (deferred shading) is a screen space lighting technique.\nInstead of calculating the lighting for a scene while you traverse its\ngeometry—you defer or wait to perform the lighting calculations until\nafter the scene's geometry fragments have been culled or discarded.\nThis can give you a performance boost depending on the complexity of your scene.\n\n### Phases\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uTimQJj.gif\" alt=\"G-buffer\" title=\"G-buffer\">\n</p>\n\nDeferred rendering is performed in two phases.\nThe first phase involves going through the scene's geometry and rendering its\npositions or depths,\nnormals,\nand materials\ninto a framebuffer known as the geometry buffer or G-buffer.\nWith the exception of some transformations,\nthis is mostly a read-only phase so its performance cost is minimal.\nAfter this phase, you're only dealing with 2D textures in the shape of the screen.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/frYp6to.gif\" alt=\"Lighting Phase\" title=\"Lighting Phase\">\n</p>\n\nThe second and last phase is where you perform your lighting calculations using the output of the first phase.\nThis is when you calculate the ambient, diffuse, and specular colors.\nShadow and normal mapping are performed in this phase as well.\n\n### Advantages\n\nThe reason for using deferred rendering is to reduce the number of lighting calculations made.\nWith forward rendering, the number of lighting calculations scales with the number of fragments and lights.\nHowever, with deferred shading, the number of lighting calculations scales with the number of pixels and lights.\nRecall that for a single pixel, there can be multiple fragments produced.\nAs you add geometry,\nthe number of lighting calculations per pixel increases when using forward but not when using deferred.\n\nFor simple scenes,\ndeferred rendering doesn't provide much of a performance boost and may even hurt performance.\nHowever, for complex scenes with lots of lighting, it becomes the better option.\nDeferred becomes faster than forward because you're only calculating the lighting per light, per pixel.\nIn forward rendering,\nyou're calculating the lighting per light per fragment which can be multiple times per pixel.\n\n### Disadvantages\n\nDeferred rendering allows you render complex scenes using many lights but it does come with its own set of tradeoffs.\nTransparency becomes an issue because the geometry data you could see through a semitransparent object is discarded in the first phase.\nOther tradesoffs include increased memory consumption due to the G-buffer and the extra workarounds needed to deal with aliasing.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](normal-mapping.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](fog.md)\n"
  },
  {
    "path": "sections/depth-of-field.md",
    "content": "[:arrow_backward:](outlining.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](posterization.md)\n\n# 3D Game Shaders For Beginners\n\n## Depth Of Field\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/DEa77Bh.gif\" alt=\"Depth Of Field\" title=\"Depth Of Field\">\n</p>\n\nLike [SSAO](ssao.md), depth of field is an effect you can't live without once you've used it.\nArtistically, you can use it to draw your viewer's eye to a certain subject.\nBut in general, depth of field adds a lot of realism for a little bit of effort.\n\n### In Focus\n\nThe first step is to render your scene completely in focus.\nBe sure to render this into a framebuffer texture.\nThis will be one of the inputs to the depth of field shader.\n\n### Out Of Focus\n\nThe second step is to blur the scene as if it was completely out of focus.\nLike bloom and SSAO, you can use a [box blur](blur.md#box-blur).\nBe sure to render this out-of-focus-scene into a framebuffer texture.\nThis will be one of the inputs to the depth of field shader.\n\n#### Bokeh\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/aQ9Ga8J.gif\" alt=\"Bokeh\" title=\"Bokeh\">\n</p>\n\nFor a great bokeh effect, dilate the out of focus texture and use that as the out of focus input.\nSee [dilation](dilation.md) for more details.\n\n### Mixing\n\n```c\n  // ...\n\n  float minDistance = 1.0;\n  float maxDistance = 3.0;\n\n  // ...\n```\n\nFeel free to tweak these two parameters.\nAll positions at or below `minDistance` will be completely in focus.\nAll positions at or beyond `maxDistance` will be completely out of focus.\n\n```c\n  // ...\n\n  vec4 focusColor      = texture(focusTexture, texCoord);\n  vec4 outOfFocusColor = texture(outOfFocusTexture, texCoord);\n\n  // ...\n```\n\nYou'll need the in focus and out of focus colors.\n\n```c\n  // ...\n\n  vec4 position = texture(positionTexture, texCoord);\n\n  // ...\n```\n\nYou'll also need the vertex position in view space.\nYou can reuse the position framebuffer texture you used for [SSAO](ssao.md#vertex-positions).\n\n```c\n  // ...\n\n  vec4 focusPoint = texture(positionTexture, mouseFocusPoint);\n\n  // ...\n```\n\nThe focus point is a position somewhere in your scene.\nAll of the points in your scene are measured from the focus point.\n\nChoosing the focus point is up to you.\nThe demo uses the scene position directly under the mouse when clicking the middle mouse button.\nHowever, it could be a constant distance from the camera or a static position.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/idDZr62.png\" alt=\"smoothstep\" title=\"smoothstep\">\n</p>\n\n```c\n  // ...\n\n  float blur =\n    smoothstep\n      ( minDistance\n      , maxDistance\n      , abs(position.y - focusPoint.y)\n      );\n\n  // ...\n```\n\n`smoothstep` returns values from zero to one.\nThe `minDistance` is the left-most edge.\nAny position less than the minimum distance, from the focus point, will be in focus or have a blur of zero.\nThe `maxDistance` is the right-most edge.\nAny position greater than the maximum distance, from the focus point, will be out of focus or have a blur or one.\nFor distances between the edges,\nblur will be between zero and one.\nThese values are interpolated along a s-shaped curve.\n\n```c\n\n  // ...\n\n  fragColor = mix(focusColor, outOfFocusColor, blur);\n\n  // ...\n```\n\nThe `fragColor` is a mixture of the in focus and out of focus color.\nThe closer `blur` is to one, the more it will use the `outOfFocusColor`.\nZero `blur` means this fragment is entirely in focus.\nAt `blur >= 1`, this fragment is completely out of focus.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [box-blur.frag](../demonstration/shaders/fragment/box-blur.frag)\n- [depth-of-field.frag](../demonstration/shaders/fragment/depth-of-field.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](outlining.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](posterization.md)\n"
  },
  {
    "path": "sections/dilation.md",
    "content": "[:arrow_backward:](sharpen.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](film-grain.md)\n\n# 3D Game Shaders For Beginners\n\n## Dilation\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/z751O74.gif\" alt=\"Dilation\" title=\"Dilation\">\n</p>\n\nDilation dilates or enlarges the brighter areas of an image while at the same time,\ncontracts or shrinks the darker areas of an image.\nThis tends to create a pillowy look.\nYou can use dilation for a glow/bloom effect or to add bokeh to your [depth of field](depth-of-field.md).\n\n```c\n  // ...\n\n  int   size         = int(parameters.x);\n  float separation   =     parameters.y;\n  float minThreshold = 0.1;\n  float maxThreshold = 0.3;\n\n  // ...\n```\n\nThe `size` and `separation` parameters control how dilated the image becomes.\nA larger `size` will increase the dilation at the cost of performance.\nA larger `separation` will increase the dilation at the cost of quality.\nThe `minThreshold` and `maxThreshold` parameters control which parts of the image become dilated.\n\n```c\n  // ...\n\n  vec2 texSize   = textureSize(colorTexture, 0).xy;\n  vec2 fragCoord = gl_FragCoord.xy;\n\n  fragColor = texture(colorTexture, fragCoord / texSize);\n\n  // ...\n```\n\nSample the color at the current fragment's position.\n\n```c\n  // ...\n\n  float  mx = 0.0;\n  vec4  cmx = fragColor;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      // ...\n    }\n  }\n\n  // ...\n```\n\nLoop through a `size` by `size` window, centered at the current fragment position.\nAs you loop, find the brightest color based on the surrounding greyscale values.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/X3uIyIL.png\" alt=\"Dilation Window\" title=\"Dilation Window\">\n</p>\n\n```c\n      // ...\n\n      // For a rectangular shape.\n      //if (false);\n\n      // For a diamond shape;\n      //if (!(abs(i) <= size - abs(j))) { continue; }\n\n      // For a circular shape.\n      if (!(distance(vec2(i, j), vec2(0, 0)) <= size)) { continue; }\n\n      // ...\n```\n\nThe window shape will determine the shape of the dilated parts of the image.\nFor a rectangular shape, you can use every fragment covered by the window.\nFor any other shape, skip the fragments that fall outside the desired shape.\n\n```c\n      // ...\n\n      vec4 c =\n        texture\n          ( colorTexture\n          ,   ( gl_FragCoord.xy\n              + (vec2(i, j) * separation)\n              )\n            / texSize\n          );\n\n      // ...\n```\n\nSample a fragment color from the surrounding window.\n\n```c\n      // ...\n\n      float mxt = dot(c.rgb, vec3(0.21, 0.72, 0.07));\n\n      // ...\n```\n\nConvert the sampled color to a greyscale value.\n\n```c\n      // ...\n\n      if (mxt > mx) {\n        mx  = mxt;\n        cmx = c;\n      }\n\n      // ...\n```\n\nIf the sampled greyscale value is larger than the current maximum greyscale value,\nupdate the maximum greyscale value and its corresponding color.\n\n```c\n  // ...\n\n  fragColor.rgb =\n    mix\n      ( fragColor.rgb\n      , cmx.rgb\n      , smoothstep(minThreshold, maxThreshold, mx)\n      );\n\n  // ...\n```\n\nThe new fragment color is a mixture between the existing fragment color and\nthe brightest color found.\nIf the maximum greyscale value found is less than `minThreshold`,\nthe fragment color is unchanged.\nIf the maximum greyscale value is greater than `maxThreshold`,\nthe fragment color is replaced with the brightest color found.\nFor any other case,\nthe fragment color is a mix between the current fragment color and the brightest color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [dilation.frag](../demonstration/shaders/fragment/dilation.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](sharpen.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](film-grain.md)\n"
  },
  {
    "path": "sections/film-grain.md",
    "content": "[:arrow_backward:](dilation.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](lookup-table.md)\n\n# 3D Game Shaders For Beginners\n\n## Film Grain\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ct7mTv5.gif\" alt=\"Film Grain\" title=\"Film Grain\">\n</p>\n\nFilm grain (when applied in subtle doses, unlike here)\ncan add a bit of realism you don't notice until it's removed.\nTypically, it's the imperfections that make a digitally generated image more believable.\nIn terms of the shader graph, film grain is usually the last effect applied before the game is put on screen.\n\n### Amount\n\n```c\n  // ...\n\n  float amount = 0.1;\n\n  // ...\n```\n\nThe `amount` controls how noticeable the film grain is.\nCrank it up for a snowy picture.\n\n### Random Intensity\n\n```c\n// ...\n\nuniform float osg_FrameTime;\n\n  //...\n\n  float toRadians = 3.14 / 180;\n\n    //...\n\n    float randomIntensity =\n      fract\n        ( 10000\n        * sin\n            (\n              ( gl_FragCoord.x\n              + gl_FragCoord.y\n              * osg_FrameTime\n              )\n            * toRadians\n            )\n        );\n\n    // ...\n```\n\nThis snippet calculates the random intensity needed to adjust the amount.\n\n```c\nTime Since F1 = 00 01 02 03 04 05 06 07 08 09 10\nFrame Number  = F1    F3    F4       F5 F6\nosg_FrameTime = 00    02    04       07 08\n```\n\n`osg_FrameTime` is\n[provided](https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L930)\nby Panda3D.\nThe frame time is a timestamp of how many seconds have passed since the first frame.\nThe example code uses this to animate the film grain as `osg_FrameTime` will always be different each frame.\n\n```c\n\n              // ...\n\n              ( gl_FragCoord.x\n              + gl_FragCoord.y\n              * 8009 // Large number here.\n\n              // ...\n```\n\nFor static film grain, replace `osg_FrameTime` with a large number.\nYou may have to try different numbers to avoid seeing any patterns.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/xqSIMCb.gif\" alt=\"Horizontal, vertical, and diagonal lines.\" title=\"Horizontal, vertical, and diagonal lines.\">\n</p>\n\n```c\n        // ...\n\n        * sin\n            (\n              ( gl_FragCoord.x\n              + gl_FragCoord.y\n              * someNumber\n\n              // ...\n```\n\nBoth the x and y coordinate are used to create points or specs of film grain.\nIf only x was used, there would only be vertical lines.\nSimilarly, if only y was used, there would be only horizontal lines.\n\nThe reason the snippet multiplies one coordinate by some number is to break up the diagonal symmetry.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4UXllmS.gif\" alt=\"Rain\" title=\"Rain\">\n</p>\n\nYou can of course remove the coordinate multiplier for a somewhat decent looking rain effect.\nTo animate the rain effect, multiply the output of `sin` by `osg_FrameTime`.\n\n```c\n              // ...\n\n              ( gl_FragCoord.x\n              + gl_FragCoord.y\n\n              // ...\n```\n\nPlay around with the x and y coordinate to try and get the rain to change directions.\nKeep only the x coordinate for a straight downpour.\n\n```c\ninput = (gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime) * toRadians\n  frame(10000 * sin(input)) =\n    fract(10000 * sin(6.977777777777778)) =\n      fract(10000 * 0.6400723818964882) =\n```\n\n`sin` is used as a hashing function.\nThe fragment's coordinates are hashed to some output of `sin`.\nThis has the nice property that no matter the input (big or small), the output range is negative one to one.\n\n```c\nfract(10000 * sin(6.977777777777778)) =\n  fract(10000 * 0.6400723818964882) =\n    fract(6400.723818964882) =\n      0.723818964882\n```\n\n`sin` is also used as a pseudo random number generator when combined with `fract`.\n\n```python\n>>> [floor(fract(4     * sin(x * toRadians)) * 10) for x in range(0, 10)]\n[0, 0, 1, 2, 2, 3, 4, 4, 5, 6]\n\n>>> [floor(fract(10000 * sin(x * toRadians)) * 10) for x in range(0, 10)]\n[0, 4, 8, 0, 2, 1, 7, 0, 0, 5]\n```\n\nTake a look at the first sequence of numbers and then the second.\nEach sequence is deterministic but the second sequence has less of a pattern than the first.\nSo while the output of `fract(10000 * sin(...))` is deterministic, it doesn't have much of a discernible pattern.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Mtt8BNg.gif\" alt=\"Increasing the pseudo randomness.\" title=\"Increasing the pseudo randomness.\">\n</p>\n\nHere you see the `sin` multiplier going from 1, to 10, to 100, and then to 1000.\n\nAs you increase the `sin` output multiplier, you get less and less of a pattern.\nThis is the reason the snippet multiplies `sin` by 10,000.\n\n### Fragment Color\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 color = texture(colorTexture, texCoord);\n\n  // ...\n```\n\nConvert the fragment's coordinates to UV coordinates.\nUsing these UV coordinates, look up the texture color for this fragment.\n\n```c\n    // ...\n\n    amount *= randomIntensity;\n\n    color.rgb += amount;\n\n    // ...\n```\n\nAdjust the amount by the random intensity and add this to the color.\n\n```c\n  // ...\n\n  fragColor = color;\n\n  // ...\n```\n\nSet the fragment color and you're done.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [film-grain.frag](../demonstration/shaders/fragment/film-grain.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](dilation.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](lookup-table.md)\n"
  },
  {
    "path": "sections/flow-mapping.md",
    "content": "[:arrow_backward:](foam.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](outlining.md)\n\n# 3D Game Shaders For Beginners\n\n## Flow Mapping\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3WDO9xW.gif\" alt=\"Flow Mapping\" title=\"Flow Mapping\">\n</p>\n\nFlow mapping is useful when you need to animate some fluid material.\nMuch like diffuse maps map UV coordinates to diffuse colors and normal maps map UV coordinates to normals,\nflow maps map UV coordinates to 2D translations or flows.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/b9Vw94N.png\" alt=\"Flow Map\" title=\"Flow Map\">\n</p>\n\nHere you see a flow map that maps UV coordinates to translations in the positive y-axis direction.\nFlow maps use the red and green channels to store translations in the x and y direction.\nThe red channel is for the x-axis and the green channel is the y-axis.\nBoth range from zero to one which translates to flows that range from `(-1, -1)` to `(1, 1)`.\nThis flow map is all one color consisting of 0.5 red and 0.6 green.\n\n```c\n[r, g, b] =\n  [r * 2 - 1, g * 2 - 1, b * 2 - 1] =\n    [ x, y, z]\n```\n\nRecall how the colors in a normal map are converted to actual normals.\nThere is a similar process for flow maps.\n\n```c\n// ...\n\nuniform sampler2D flowTexture;\n\n  vec2 flow = texture(flowTexture, uv).xy;\n       flow = (flow - 0.5) * 2;\n\n  // ...\n```\n\nTo convert a flow map color to a flow,\nyou minus 0.5 from the channel (red and green) and multiply by two.\n\n```c\n(r, g) =\n ( (r - 0.5) * 2\n , (g - 0.5) * 2\n ) =\n  ( (0.5 - 0.5) * 2\n  , (0.6 - 0.5) * 2\n  ) =\n    (x, y) =\n      (0, 0.2)\n```\n\nThe flow map above maps each UV coordinate to the flow `(0, 0.2)`.\nThis indicates zero movement in the x direction and a movement of 0.2 in the y direction.\n\nThe flows can be used to translate all sorts of things but they're typically used to\noffset the UV coordinates of a another texture.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/N6TWBw8.gif\" alt=\"Foam Mask\" title=\"Foam Mask\">\n</p>\n\n```c\n  // ...\n\n  vec2 flow = texture(flowTexture, diffuseCoord).xy;\n       flow = (flow - 0.5) * 2;\n\n  vec4 foamPattern =\n    texture\n      ( foamPatternTexture\n      , vec2\n          ( diffuseCoord.x + flow.x * osg_FrameTime\n          , diffuseCoord.y + flow.y * osg_FrameTime\n          )\n      );\n\n  // ...\n```\n\nFor example, the demo program uses a flow map to animate the water.\nHere you see the flow map being used to animate the\n[foam mask](foam.md#mask).\nThis continuously moves the diffuse UV coordinates directly up,\ngiving the foam mask the appearance of moving down stream.\n\n```c\n          // ...\n\n          ( diffuseCoord.x + flow.x * osg_FrameTime\n          , diffuseCoord.y + flow.y * osg_FrameTime\n\n          // ...\n```\n\nYou'll need how many seconds have passed since the first frame\nin order to animate the UV coordinates in the direction indicated by the flow.\n`osg_FrameTime` is\n[provided](https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L930)\nby Panda3D.\nIt is a timestamp of how many seconds have passed since the first frame.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [normal.frag](../demonstration/shaders/fragment/normal.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](foam.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](outlining.md)\n"
  },
  {
    "path": "sections/foam.md",
    "content": "[:arrow_backward:](screen-space-refraction.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](flow-mapping.md)\n\n# 3D Game Shaders For Beginners\n\n## Foam\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/SVLPYKn.gif\" alt=\"Foam\" title=\"Foam\">\n</p>\n\nFoam is typically used when simulating some body of water.\nAnywhere the water's flow is disrupted, you add some foam.\nThe foam isn't much by itself but it can really connect the water with the rest of the scene.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/HCqvd8c.gif\" alt=\"Lava River\" title=\"Lava River\">\n</p>\n\nBut don't stop at just water.\nYou can use the same technique to make a river of lava for example.\n\n### Vertex Positions\n\nLike\n[screen space refraction](screen-space-refraction.md),\nyou'll need both the foreground and background vertex positions.\nThe foreground being the scene with the foamy surface\nand the background being the scene without the foamy surface.\nReferrer back to [SSAO](ssao.md#vertex-positions) for the details\non how to acquire the vertex positions in view space.\n\n### Mask\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/N6TWBw8.gif\" alt=\"Foam Mask\" title=\"Foam Mask\">\n</p>\n\nYou'll need to texture your scene with a foam mask.\nThe demo masks everything off except the water.\nFor the water, it textures it with a foam pattern.\n\n```c\n// ...\n\nuniform sampler2D foamPatternTexture;\n\nin vec2 diffuseCoord;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec4 foamPattern = texture(foamPatternTexture, diffuseCoord);\n\n  fragColor = vec4(vec3(dot(foamPattern.rgb, vec3(1)) / 3), 1);\n}\n```\n\nHere you see the fragment shader that generates the foam mask.\nIt takes a foam pattern texture and UV maps it to the scene's geometry using the diffuse UV coordinates.\nFor every model, except the water, the shader is given a solid black texture as the `foamPatternTexture`.\n\n```c\n  // ...\n\n  fragColor = vec4(vec3(dot(foamPattern.rgb, vec3(1)) / 3), 1);\n\n  // ...\n```\n\nThe fragment color is converted to greyscale,\nas a precaution,\nsince the foam shader expects the foam mask to be greyscale.\n\n### Uniforms\n\n```c\n// ...\n\nuniform sampler2D maskTexture;\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\n\n// ...\n```\n\nThe foam shader accepts a mask texture,\nthe foreground vertex positions (`positionFromTexture`),\nand the background vertex positions (`positionToTexture`).\n\n### Parameters\n\n```c\n  // ...\n\n  float foamDepth = 4;\n  vec4  foamColor = vec4(0.8, 0.85, 0.92, 1);\n\n  // ...\n```\n\nThe adjustable parameters for the foam shader are the foam depth and color.\nThe foam depth controls how much foam is shown.\nAs the foam depth increases, the amount of foam shown increases.\n\n### Distance\n\n```c\n  // ...\n\n  vec4 positionFrom = texture(positionFromTexture, texCoord);\n  vec4 positionTo   = texture(positionToTexture,   texCoord);\n\n  float depth  = (positionTo.xyz - positionFrom.xyz).y;\n\n  // ...\n```\n\nCompute the distance from the foreground position to the background position.\nSince the positions are in view (camera) space, we only need the y value since it goes into the screen.\n\n### Amount\n\n```c\n  // ...\n\n  float amount  = clamp(depth / foamDepth.x, 0, 1);\n        amount  = 1 - amount;\n        amount *= mask.r;\n        amount  = amount * amount / (2 * (amount * amount - amount) + 1);\n\n  // ...\n```\n\nThe amount of foam is based on the depth, the foam depth parameter, and the mask value.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/CDIPmin.png\" alt=\"Easing equation.\" title=\"Easing equation.\">\n</p>\n\n```c\n        // ...\n\n        amount  = amount * amount / (2 * (amount * amount - amount) + 1);\n\n        // ...\n```\n\nReshape the amount using the ease in and out easing function.\nThis will give a lot of foam near depth zero and little to no foam near `foamDepth`.\n\n### Fragment Color\n\n```c\n  // ...\n\n  fragColor = mix(vec4(0), foamColor, amount);\n\n  // ...\n```\n\nThe fragment color is a mix between transparent black and the foam color based on the amount.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [foam-mask.frag](../demonstration/shaders/fragment/foam-mask.frag)\n- [foam.frag](../demonstration/shaders/fragment/foam.frag)\n- [base-combine.frag](../demonstration/shaders/fragment/base-combine.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](screen-space-refraction.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](flow-mapping.md)\n"
  },
  {
    "path": "sections/fog.md",
    "content": "[:arrow_backward:](deferred-rendering.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](blur.md)\n\n# 3D Game Shaders For Beginners\n\n## Fog\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uNRxZl4.gif\" alt=\"Fog\" title=\"Fog\">\n</p>\n\nFog (or mist as it's called in Blender) adds atmospheric haze to a scene,\nproviding mystique and softening pop-ins (geometry suddenly entering into the camera's frustum).\n\n```c\n// ...\n\nuniform vec4 color;\n\nuniform vec2 nearFar;\n\n// ...\n```\n\nTo calculate the fog, you'll need its color, near distance, and far distance.\n\n```c\n// ...\n\nuniform sampler2D positionTexture;\n\n  // ...\n\n  vec2 texSize  = textureSize(positionTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 position = texture(positionTexture, texCoord);\n\n  // ...\n```\n\nIn addition to the fog's attributes, you'll also need the fragment's vertex `position`.\n\n```c\n  float fogMin = 0.00;\n  float fogMax = 0.97;\n```\n\n`fogMax` controls how much of the scene is still visible when the fog is most intense.\n`fogMin` controls how much of the fog is still visible when the fog is least intense.\n\n```c\n  // ...\n\n  float near = nearFar.x;\n  float far  = nearFar.y;\n\n  float intensity =\n    clamp\n      (   (position.y - near)\n        / (far        - near)\n      , fogMin\n      , fogMax\n      );\n\n  // ...\n```\n\nThe example code uses a linear model for calculating the fog's intensity.\nThere's also an exponential model you could use.\n\nThe fog's intensity is `fogMin` before or at the start of the fog's `near` distance.\nAs the vertex `position` gets closer to the end of the fog's `far` distance, the `intensity` moves closer to `fogMax`.\nFor any vertexes after the end of the fog, the `intensity` is clamped to `fogMax`.\n\n```c\n  // ...\n\n  fragColor = vec4(color.rgb, intensity);\n\n  // ...\n```\n\nSet the fragment's color to the fog `color` and the fragment's alpha channel to the `intensity`.\nAs `intensity` gets closer to one, you'll have less of the scene's color and more of the fog color.\nWhen `intensity` reaches one, you'll have all fog color and nothing else.\n\n```c\n// ...\n\nuniform sampler2D baseTexture;\nuniform sampler2D fogTexture;\n\n  // ...\n\n  vec2 texSize  = textureSize(baseTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 baseColor = texture(baseTexture, texCoord);\n  vec4 fogColor  = texture(fogTexture,  texCoord);\n\n  fragColor = baseColor;\n\n  // ...\n\n  fragColor = mix(fragColor, fogColor, min(fogColor.a, 1));\n\n  // ...\n```\n\nNormally you calculate the fog in the same shader that does the lighting calculations.\nHowever, you can also break it out into its own framebuffer texture.\nHere you see the fog color being applied to the rest of the scene much like you would apply a layer in GIMP.\nThis allows you to calculate the fog once instead calculating it in every shader that eventually needs it.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [normal.frag](../demonstration/shaders/fragment/normal.frag)\n- [fog.frag](../demonstration/shaders/fragment/fog.frag)\n- [outline.frag](../demonstration/shaders/fragment/outline.frag)\n- [scene-combine.frag](../demonstration/shaders/fragment/scene-combine.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](deferred-rendering.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](blur.md)\n"
  },
  {
    "path": "sections/fresnel-factor.md",
    "content": "[:arrow_backward:](blinn-phong.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](rim-lighting.md)\n\n# 3D Game Shaders For Beginners\n\n## Fresnel Factor\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3lQL51m.gif\" alt=\"Fresnel\" title=\"Fresnel\">\n</p>\n\nThe fresnel factor alters the reflectiveness of a surface based on the camera or viewing angle.\nAs a surface points away from the camera, its reflectiveness goes up.\nSimilarly, as a surface points towards the camera, its reflectiveness goes down.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WolRRhX.png\" alt=\"\" title=\"\">\n</p>\n\nIn other words, as a surface becomes perpendicular with the camera, it becomes more mirror like.\nUtilizing this property, you can vary the opacity of reflections\n(such as [specular](lighting.md#specular) and [screen space reflections](screen-space-reflection.md))\nand/or vary a surface's alpha values for a more plausible or realistic look.\n\n### Specular Reflection\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FnOhXxv.gif\" alt=\"Specular Intensity\" title=\"Specular Intensity\">\n</p>\n\n```c\n  vec4 specular =   materialSpecularColor\n                  * lightSpecularColor\n                  * pow(max(dot(eye, reflection), 0.0), shininess);\n```\n\nIn the [lighting](lighting.md#specular) section,\nthe specular component was a combination of the\nmaterial's specular color,\nthe light's specular color,\nand by how much the camera pointed into the light's reflection direction.\nIncorporating the fresnel factor,\nyou'll now vary the material specular color based on the angle between the camera and the surface it's pointed at.\n\n```c\n  // ...\n\n  vec3 eye = normalize(-vertexPosition.xyz);\n\n  // ...\n```\n\nThe first vector you'll need is the eye/view/camera vector.\nRecall that the eye vector points from the vertex position to the camera's position.\nIf the vertex position is in view or camera space,\nthe eye vector is the vertex position pointed in the opposite direction.\n\n```c\n  // ...\n\n  vec3 light   = normal(lightPosition.xyz - vertexPosition.xyz);\n  vec3 halfway = normalize(light + eye);\n\n  // ...\n```\n\nThe fresnel factor is calculated using two vectors.\nThe simplest two vectors to use are the eye and normal vector.\nHowever, if you're using the halfway vector (from the [Blinn-Phong](blinn-phong.md) section),\nyou can instead calculate the fresnel factor using the halfway and eye vector.\n\n```c\n\n  // ...\n\n  float fresnelFactor = dot(halfway, eye); // Or dot(normal, eye).\n        fresnelFactor = max(fresnelFactor, 0.0);\n        fresnelFactor = 1.0 - fresnelFactor;\n        fresnelFactor = pow(fresnelFactor, fresnelPower);\n\n  // ...\n```\n\nWith the needed vectors in hand,\nyou can now compute the fresnel factor.\nThe fresnel factor ranges from zero to one.\nWhen the dot product is one,\nthe fresnel factor is zero.\nWhen the dot product is less than or equal to zero,\nthe fresnel factor is one.\nThis equation comes from\n[Schlick's approximation](https://en.wikipedia.org/wiki/Schlick%27s_approximation).\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AAFI8p1.gif\" alt=\"Fresnel Power\" title=\"Fresnel Power\">\n</p>\n\nIn Schlick's approximation,\nthe `fresnelPower` is five but you can alter this to your liking.\nThe demo code varies it using the blue channel of the specular map with a maximum value of five.\n\n```c\n  // ...\n\n  materialSpecularColor.rgb = mix(materialSpecularColor.rgb, vec3(1.0), fresnelFactor);\n\n  // ...\n```\n\nOnce the fresnel factor is determined,\nuse it to modulate the material's specular color.\nAs the fresnel factor approaches one,\nthe material becomes more like a mirror or fully reflective.\n\n```c\n  // ...\n\n  vec4 specular      = vec4(vec3(0.0), 1.0);\n       specular.rgb  =   materialSpecularColor.rgb\n                       * lightSpecularColor.rgb\n                       * pow\n                          ( max(dot(normal, halfway), 0.0) // Or max(dot(reflection, eye), 0.0).\n                          , shininess\n                          );\n\n  // ...\n```\n\nAs before,\nthe specular component is a combination of the\nmaterial's specular color,\nthe light's specular color,\nand by how much the camera points into the direction of the light's reflection.\nHowever,\nusing the fresnel factor,\nthe material's specular color various depending on the orientation of the camera and the surface it's looking at.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](blinn-phong.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](rim-lighting.md)\n"
  },
  {
    "path": "sections/gamma-correction.md",
    "content": "[:arrow_backward:](lookup-table.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](setup.md)\n\n# 3D Game Shaders For Beginners\n\n## Gamma Correction\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IG7A6cj.gif\" alt=\"Gamma Correction\" title=\"Gamma Correction\">\n</p>\n\nCorrecting for gamma will make your color calculations look correct.\nThis isn't to say they'll look amazing but with gamma correction,\nyou'll find that the colors meld together better,\nthe shadows are more nuanced,\nand the highlights are more subtle.\nWithout gamma correction,\nthe shadowed areas tend to get crushed while the highlighted areas tend to get blown-out and\nover saturated making for a harsh contrast overall.\n\nIf you're aiming for realism,\ngamma correction is especially important.\nAs you perform more and more calculations,\nthe tiny errors add up making it harder to achieve photorealism.\nThe equations will be correct but the inputs and outputs will be wrong leaving you frustrated.\n\nIt's easy to get twisted around when thinking about gamma correction\nbut essentially it boils down to knowing what color space a color is in and how to convert that color to the color space you need.\nWith those two pieces of the puzzle,\ngamma correction becomes a tedious yet simple chore you'll have to perform from time to time.\n\n### Color Spaces\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/a1U5oBq.png\" alt=\"sRGB vs RGB\" title=\"sRGB vs RGB\">\n</p>\n\nThe two color spaces you'll need to be aware of are sRGB (standard Red Green Blue) and RGB or linear color space.\n\n```bash\nidentify -format \"%[colorspace]\\n\" house-diffuse-srgb.png\nsRGB\n\nidentify -format \"%[colorspace]\\n\" house-diffuse-rgb.png\nRGB\n```\n\nKnowing what color space a color texture is in will determine how you handle it in your shaders.\nTo determine the color space of a texture, use ImageMagick's `identify`.\nYou'll find that most textures are in sRGB.\n\n```bash\nconvert house-diffuse-srgb -colorspace rgb house-diffuse-rgb.png\n```\n\nTo convert a texture to a particular color space, use ImageMagick's `convert` program.\nNotice how a texture is darkened when transforming from sRGB to RGB.\n\n### Decoding\n\n\nThe red, green, and blue values in a sRGB color texture are encoded and cannot be modified directly.\nModifying them directly would be like running spellcheck on an encrypted message.\nBefore you can run spellcheck,\nyou first have to decrypt the message.\nSimilarly,\nto modify the values of an sRGB texture,\nyou first have to decode or transform them to RGB or linear color space.\n\n```c\n  // ...\n\n  color     = texture(color_texture, uv);\n  color.rgb = pow(color.rgb, 2.2);\n\n  // ...\n```\n\nTo decode a sRGB encoded color,\nraise the `rgb` values to the power of `2.2`.\nOnce you have decoded the color,\nyou are now free to add, subtract, multiply, and divide it.\n\nBy raising the color values to the power of `2.2`,\nyou're converting them from sRGB to RGB or linear color space.\nThis conversion has the effect of darkening the colors.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/E5nkRfG.png\" alt=\"Color Darkening\" title=\"Color Darkening\">\n</p>\n\nFor example,\n`vec3(0.9, 0.2, 0.3)` becomes `vec3(0.793, 0.028, 0.07)`.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/TOEb0EC.gif\" alt=\"Gamma Curves\" title=\"Gamma Curves\">\n</p>\n\nThe `2.2` value is known as gamma.\nLoosely speaking, gamma can either be `1.0 / 2.2`, `2.2`, or `1.0`.\nAs you've seen, `2.2` is for decoding sRGB encoded color textures.\nAs you will see, `1.0 / 2.2` is for encoding linear or RGB color textures.\nAnd `1.0` is RGB or linear color space since `y = 1 * x + 0` and\nany base raised to the power of `1.0` is itself.\n\n#### Non-color Data\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/reA2qjs.png\" alt=\"Non-color Data\" title=\"Non-color Data\">\n</p>\n\nOne important exception to decoding is when the \"colors\" of a texture represent non-color data.\nSome examples of non-color data would be the normals in a normal map,\nthe alpha channel,\nthe heights in a height map,\nand the directions in a flow map.\nOnly decode color related data or data that represents color.\nWhen dealing with non-color data,\ntreat the sRGB color values as RGB or linear and skip the decoding process.\n\n### Encoding\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tRxkKNe.gif\" alt=\"Perceptually versus Actually Linear\" title=\"Perceptually versus Actually Linear\">\n</p>\n\nThe necessity for encoding and decoding stems from the fact that humans do not perceive lightness linearly and\nmost displays (like a monitor) lack the precision or number of bits to accurately show both lighter and darker tonal values or shades.\nWith only so many bits to go around,\ncolors are encoded in such a way that more bits are devoted to the darker shades than the lighter shades\nsince humans are more sensitive to darker tones than lighter tones.\nEncoding it this way uses the limited number of bits more effectively for human perception.\nStill, the only thing to remember is that your display is expecting sRGB encoded values.\nTherefore, if you decoded a sRGB value, you have to encode it before it makes its way to your display.\n\n```c\n  // ...\n\n  color     = texture(color_texture, uv);\n  color.rgb = pow(color.rgb, 1.0 / 2.2);\n\n  // ...\n```\n\nTo encode a linear value or convert RGB to sRGB,\nraise the `rgb` values to the power of `1.0 / 2.2`.\nNotice how `1.1 / 2.2` is the reciprocal of `2.2` or `2.2 / 1.0`.\nHere you see the symmetry in decoding and encoding.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4km0pdv.gif\" alt=\"Not Gamma Corrected versus Gamma Corrected\" title=\"Not Gamma Corrected versus Gamma Corrected\">\n</p>\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [gamma-correction.frag](../demonstration/shaders/fragment/gamma-correction.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](lookup-table.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](setup.md)\n"
  },
  {
    "path": "sections/glsl.md",
    "content": "[:arrow_backward:](reference-frames.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](render-to-texture.md)\n\n# 3D Game Shaders For Beginners\n\n## GLSL\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7b5MCBG.gif\" alt=\"\" title=\"\">\n</p>\n\nInstead of using the\n[fixed-function](https://en.wikipedia.org/wiki/Fixed-function)\npipeline,\nyou'll be using the programmable GPU rendering pipeline.\nSince it is programmable, it is up to you to supply the programming in the form of shaders.\nA shader is a (typically small) program you write using a syntax reminiscent of C.\nThe programmable GPU rendering pipeline has various different stages that you can program with shaders.\nThe different types of shaders include vertex, tessellation, geometry, fragment, and compute.\nYou'll only need to focus on the vertex and fragment stages for the techniques below.\n\n```c\n#version 150\n\nvoid main() {}\n```\n\nHere is a bare-bones GLSL shader consisting of the GLSL version number and the main function.\n\n```c\n#version 150\n\nuniform mat4 p3d_ModelViewProjectionMatrix;\n\nin vec4 p3d_Vertex;\n\nvoid main()\n{\n  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n}\n```\n\nHere is a stripped down GLSL vertex shader that transforms an incoming vertex to clip space\nand outputs this new position as the vertex's homogeneous position.\nThe `main` procedure doesn't return anything since it is `void` and the `gl_Position` variable is a built-in output.\n\nTake note of the keywords `uniform` and `in`.\nThe `uniform` keyword means this global variable is the same for all vertexes.\nPanda3D sets the `p3d_ModelViewProjectionMatrix` for you and it is the same matrix for each vertex.\nThe `in` keyword means this global variable is being given to the shader.\nThe vertex shader receives each vertex that makes up the geometry the vertex shader is attached to.\n\n```c\n#version 150\n\nout vec4 fragColor;\n\nvoid main() {\n  fragColor = vec4(0, 1, 0, 1);\n}\n```\n\nHere is a stripped down GLSL fragment shader that outputs the fragment color as solid green.\nKeep in mind that a fragment affects at most one screen pixel but a single pixel can be affected by many fragments.\n\nTake note of the `out` keyword.\nThe `out` keyword means this global variable is being set by the shader.\nThe name `fragColor` is arbitrary so feel free to choose a different one.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/V25UzMa.gif\" alt=\"Output of the stripped down shaders.\" title=\"Output of the stripped down shaders.\">\n</p>\n\nThis is the output of the two shaders shown above.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](reference-frames.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](render-to-texture.md)\n"
  },
  {
    "path": "sections/lighting.md",
    "content": "[:arrow_backward:](texturing.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](blinn-phong.md)\n\n# 3D Game Shaders For Beginners\n\n## Lighting\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/zQrA8tr.gif\" alt=\"Lighting\" title=\"Lighting\">\n</p>\n\nCompleting the lighting involves calculating and combining the ambient, diffuse, specular, and emission light aspects.\nThe example code uses either Phong or Blinn-Phong lighting.\n\n### Vertex\n\n```c\n// ...\n\nuniform struct p3d_LightSourceParameters\n  { vec4 color\n\n  ; vec4 ambient\n  ; vec4 diffuse\n  ; vec4 specular\n\n  ; vec4 position\n\n  ; vec3  spotDirection\n  ; float spotExponent\n  ; float spotCutoff\n  ; float spotCosCutoff\n\n  ; float constantAttenuation\n  ; float linearAttenuation\n  ; float quadraticAttenuation\n\n  ; vec3 attenuation\n\n  ; sampler2DShadow shadowMap\n\n  ; mat4 shadowViewMatrix\n  ;\n  } p3d_LightSource[NUMBER_OF_LIGHTS];\n\n// ...\n```\n\nFor every light, minus the ambient light, Panda3D gives you this convenient\nstruct which is available to both the vertex and fragment shaders.\nThe biggest convenience being the shadow map and shadow view matrix for transforming vertexes to shadow or light space.\n\n```c\n  // ...\n\n  vertexPosition = p3d_ModelViewMatrix * p3d_Vertex;\n\n  // ...\n\n  for (int i = 0; i < p3d_LightSource.length(); ++i) {\n    vertexInShadowSpaces[i] = p3d_LightSource[i].shadowViewMatrix * vertexPosition;\n  }\n\n  // ...\n```\n\nStarting in the vertex shader, you'll need to transform and\noutput the vertex from view space to shadow or light space for each light in your scene.\nYou'll need this later in the fragment shader in order to render the shadows.\nShadow or light space is where every coordinate is relative to the light position (the light is the origin).\n\n### Fragment\n\nThe fragment shader is where most of the lighting calculations take place.\n\n#### Material\n\n```c\n// ...\n\nuniform struct\n  { vec4 ambient\n  ; vec4 diffuse\n  ; vec4 emission\n  ; vec3 specular\n  ; float shininess\n  ;\n  } p3d_Material;\n\n// ...\n```\n\nPanda3D gives us the material (in the form of a struct) for the mesh or model you are currently rendering.\n\n#### Multiple Lights\n\n```c\n  // ...\n\n  vec4 diffuse  = vec4(0.0, 0.0, 0.0, diffuseTex.a);\n  vec4 specular = vec4(0.0, 0.0, 0.0, diffuseTex.a);\n\n  // ...\n```\n\nBefore you loop through the scene's lights, create an accumulator for both the diffuse and specular colors.\n\n```c\n  // ...\n\n  for (int i = 0; i < p3d_LightSource.length(); ++i) {\n    // ...\n  }\n\n  // ...\n```\n\nNow you can loop through the lights, calculating the diffuse and specular colors for each one.\n\n#### Light Related Vectors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/0pzNh5d.gif\" alt=\"Phong Lighting Model\" title=\"Phong Lighting Model\">\n</p>\n\nHere you see the four major vectors you'll need to calculate the diffuse and specular colors contributed by each light.\nThe light direction vector is the light blue arrow pointing to the light.\nThe normal vector is the green arrow standing straight up.\nThe reflection vector is the dark blue arrow mirroring the light direction vector.\nThe eye or view vector is the orange arrow pointing towards the camera.\n\n```c\n    // ...\n\n    vec3 lightDirection =\n        p3d_LightSource[i].position.xyz\n      - vertexPosition.xyz\n      * p3d_LightSource[i].position.w;\n\n    // ...\n```\n\nThe light direction is from the vertex's position to the light's position.\n\nPanda3D sets `p3d_LightSource[i].position.w` to zero if this is a directional light.\nDirectional lights do not have a position as they only have a direction.\nSo if this is a directional light,\nthe light direction will be the negative or opposite direction of the light as Panda3D sets\n`p3d_LightSource[i].position.xyz` to be `-direction` for directional lights.\n\n```c\n  // ...\n\n  normal = normalize(vertexNormal);\n\n  // ...\n```\n\nYou'll need the vertex normal to be a unit vector.\nUnit vectors have a length of magnitude of one.\n\n```c\n    // ...\n\n    vec3 unitLightDirection = normalize(lightDirection);\n    vec3 eyeDirection       = normalize(-vertexPosition.xyz);\n    vec3 reflectedDirection = normalize(-reflect(unitLightDirection, normal));\n\n    // ...\n```\n\nNext you'll need three more vectors.\n\nYou'll need to take the dot product involving the light direction so its best to normalize it.\nThis gives it a distance or magnitude of one (unit vector).\n\nThe eye direction is the opposite of the vertex/fragment position since the vertex/fragment position is relative to the camera's position.\nRemember that the vertex/fragment position is in view space.\nSo instead of going from the camera (eye) to the vertex/fragment, you go from the vertex/fragment to the eye (camera).\n\nThe\n[reflection vector](http://asawicki.info/news_1301_reflect_and_refract_functions.html)\nis a reflection of the light direction at the surface normal.\nAs the light \"ray\" hits the surface, it bounces off at the same angle it came in at.\nThe angle between the light direction vector and the normal is known as the \"angle of incidence\".\nThe angle between the reflection vector and the normal is known as the \"angle of reflection\".\n\nYou'll have to negate the reflected light vector as it needs to point in the same direction as the eye vector.\nRemember the eye direction is from the vertex/fragment to the camera position.\nYou'll use the reflection vector to calculate the intensity of the specular highlight.\n\n#### Diffuse\n\n```c\n    // ...\n\n    float diffuseIntensity  = dot(normal, unitLightDirection);\n\n    if (diffuseIntensity < 0.0) { continue; }\n\n    // ...\n```\n\nThe diffuse intensity is the dot product between the surface normal and the unit vector light direction.\nThe dot product can range from negative one to one.\nIf both vectors point in the same direction, the intensity is one.\nAny other case will be less than one.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Nb78z96.gif\" alt=\"The light direction versus the normal direction.\" title=\"The light direction versus the normal direction.\">\n</p>\n\nAs the light vector approaches the same direction as the normal, the diffuse intensity approaches one.\n\n```c\n    // ...\n\n    if (diffuseIntensity < 0.0) { continue; }\n\n    // ...\n```\n\nIf the diffuse intensity is zero or less, move on to the next light.\n\n```c\n    // ...\n\n    vec4 diffuseTemp =\n      vec4\n        ( clamp\n            (   diffuseTex.rgb\n              * p3d_LightSource[i].diffuse.rgb\n              * diffuseIntensity\n            , 0\n            , 1\n            )\n        , diffuseTex.a\n        );\n\n    diffuseTemp = clamp(diffuseTemp, vec4(0), diffuseTex);\n\n    // ...\n```\n\nYou can now calculate the diffuse color contributed by this light.\nIf the diffuse intensity is one, the diffuse color will be a mix between the diffuse texture color and the lights color.\nAny other intensity will cause the diffuse color to be darker.\n\nNotice how I clamp the diffuse color to be only as bright as the diffuse texture color is.\nThis will protect the scene from being over exposed.\nWhen creating your diffuse textures, make sure to create them as if they were fully lit.\n\n#### Specular\n\nAfter diffuse, comes specular.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FnOhXxv.gif\" alt=\"Specular Intensity\" title=\"Specular Intensity\">\n</p>\n\n```c\n    // ...\n\n    float specularIntensity = max(dot(reflectedDirection, eyeDirection), 0);\n\n    vec4 specularTemp =\n      clamp\n        (   vec4(p3d_Material.specular, 1)\n          * p3d_LightSource[i].specular\n          * pow\n              ( specularIntensity\n              , p3d_Material.shininess\n              )\n        , 0\n        , 1\n        );\n\n    // ...\n```\n\nThe specular intensity is the dot product between the eye vector and the reflection vector.\nAs with the diffuse intensity, if the two vectors point in the same direction, the specular intensity is one.\nAny other intensity will diminish the amount of specular color contributed by this light.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/4r6wqLP.gif\" alt=\"Shininess\" title=\"Shininess\">\n</p>\n\nThe material shininess determines how spread out the specular highlight is.\nThis is typically set in a modeling program like Blender.\nIn Blender it's known as the specular hardness.\n\n#### Spotlights\n\n```c\n    // ...\n\n    float unitLightDirectionDelta =\n      dot\n        ( normalize(p3d_LightSource[i].spotDirection)\n        , -unitLightDirection\n        );\n\n    if (unitLightDirectionDelta < p3d_LightSource[i].spotCosCutoff) { continue; }\n\n    // ...\n}\n```\n\nThis snippet keeps fragments outside of a spotlight's cone or frustum from being affected by the light.\nFortunately, Panda3D\n[sets up](https://github.com/panda3d/panda3d/blob/daa57733cb9b4ccdb23e28153585e8e20b5ccdb5/panda/src/display/graphicsStateGuardian.cxx#L1705)\n`spotDirection` and `spotCosCutoff` to also work for directional lights and points lights.\nSpotlights have both a position and direction.\nHowever, directional lights only have a direction and point lights only have a position.\nStill, this code works for all three lights avoiding the need for noisy if statements.\n\n```c\n        // ...\n\n        , -unitLightDirection\n\n        // ...\n```\n\nYou must negate `unitLightDirection`.\n`unitLightDirection` goes from the fragment to the spotlight and you need it to go from the spotlight to the fragment\nsince the `spotDirection` goes directly down the center of the spotlight's frustum some distance away from the spotlight's position.\n\n```c\nspotCosCutoff = cosine(0.5 * spotlightLensFovAngle);\n```\n\nFor a spotlight, if the dot product between the fragment-to-light vector and the spotlight's direction vector is less than the cosine\nof half the spotlight's field of view angle, the shader disregards this light's influence.\n\nFor directional lights and point lights, Panda3D sets `spotCosCutoff` to negative one.\nRecall that the dot product ranges from negative one to one.\nSo it doesn't matter what the `unitLightDirectionDelta` is because it will always be greater than or equal to negative one.\n\n```c\n    // ...\n\n    diffuseTemp *= pow(unitLightDirectionDelta, p3d_LightSource[i].spotExponent);\n\n    // ...\n```\n\nLike the `unitLightDirectionDelta` snippet, this snippet also works for all three light types.\nFor spotlights, this will make the fragments brighter as you move closer to the center of the spotlight's frustum.\nFor directional lights and point lights, `spotExponent` is zero.\nRecall that anything to the power of zero is one so the diffuse color is one times itself meaning it is unchanged.\n\n#### Shadows\n\n```c\n    // ...\n\n    float shadow =\n      textureProj\n        ( p3d_LightSource[i].shadowMap\n        , vertexInShadowSpaces[i]\n        );\n\n    diffuseTemp.rgb  *= shadow;\n    specularTemp.rgb *= shadow;\n\n    // ...\n```\n\nPanda3D makes applying shadows relatively easy by providing the shadow map and shadow transformation matrix for every scene light.\nTo create the shadow transformation matrix yourself,\nyou'll need to assemble a matrix that transforms view space coordinates to light space (coordinates are relative to the light's position).\nTo create the shadow map yourself, you'll need to render the scene from the perspective of the light to a framebuffer texture.\nThe framebuffer texture must hold the distances from the light to the fragments.\nThis is known as a \"depth map\".\nLastly, you'll need to manually give to your shader your DIY depth map as a `uniform sampler2DShadow`\nand your DIY shadow transformation matrix as a `uniform mat4`.\nAt this point, you've recreated what Panda3D does for you automatically.\n\nThe shadow snippet shown uses `textureProj` which is different from the `texure` function shown earlier.\n`textureProj` first divides `vertexInShadowSpaces[i].xyz` by `vertexInShadowSpaces[i].w`.\nAfter this, it uses `vertexInShadowSpaces[i].xy` to locate the depth stored in the shadow map.\nNext it uses `vertexInShadowSpaces[i].z` to compare this vertex's depth against the shadow map depth at\n`vertexInShadowSpaces[i].xy`.\nIf the comparison passes, `textureProj` will return one.\nOtherwise, it will return zero.\nZero meaning this vertex/fragment is in the shadow and one meaning this vertex/fragment is not in the shadow.\n\n`textureProj` can also return a value between zero and one depending on how the shadow map was set up.\nIn this instance, `textureProj` performs multiple depth tests using neighboring depth values and returns a weighted average.\nThis weighted average can give shadows a softer look.\n\n#### Attenuation\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/jyatr7l.png\" alt=\"Attenuation\" title=\"Attenuation\">\n</p>\n\n```c\n    // ...\n\n    float lightDistance = length(lightDirection);\n\n    float attenuation =\n        1\n      / ( p3d_LightSource[i].constantAttenuation\n        + p3d_LightSource[i].linearAttenuation\n        * lightDistance\n        + p3d_LightSource[i].quadraticAttenuation\n        * (lightDistance * lightDistance)\n        );\n\n    diffuseTemp.rgb  *= attenuation;\n    specularTemp.rgb *= attenuation;\n\n    // ...\n```\n\nThe light's distance is just the magnitude or length of the light direction vector.\nNotice it's not using the normalized light direction as that distance would be one.\n\nYou'll need the light distance to calculate the attenuation.\nAttenuation meaning the light's influence diminishes as you get further away from it.\n\nYou can set `constantAttenuation`, `linearAttenuation`, and `quadraticAttenuation` to whatever values you would like.\nA good starting point is `constantAttenuation = 1`, `linearAttenuation = 0`, and `quadraticAttenuation = 1`.\nWith these settings, the attenuation is one at the light's position and approaches zero as you move further away.\n\n#### Final Light Color\n\n```c\n    // ...\n\n    diffuse  += diffuseTemp;\n    specular += specularTemp;\n\n    // ...\n```\n\nTo calculate the final light color, add the diffuse and specular together.\nBe sure to add this to the accumulator as you loop through the scene's lights.\n\n#### Ambient\n\n```c\n// ...\n\nuniform sampler2D p3d_Texture1;\n\n// ...\n\nuniform struct\n  { vec4 ambient\n  ;\n  } p3d_LightModel;\n\n// ...\n\nin vec2 diffuseCoord;\n\n  // ...\n\n  vec4 diffuseTex  = texture(p3d_Texture1, diffuseCoord);\n\n  // ...\n\n  vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex;\n\n// ...\n```\n\nThe ambient component to the lighting model is based on the material's ambient color,\nthe ambient light's color, and the diffuse texture color.\n\nThere should only ever be one ambient light.\nBecause of this, the ambient color calculation only needs to occur once.\nContrast this with the diffuse and specular color which must be accumulated for each spot/directional/point light.\nWhen you reach [SSAO](ssao.md), you'll revisit the ambient color calculation.\n\n#### Putting It All Together\n\n```c\n  // ...\n\n  vec4 outputColor = ambient + diffuse + specular + p3d_Material.emission;\n\n  // ...\n```\n\nThe final color is the sum of the ambient color, diffuse color, specular color, and the emission color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](texturing.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](blinn-phong.md)\n"
  },
  {
    "path": "sections/lookup-table.md",
    "content": "[:arrow_backward:](film-grain.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](gamma-correction.md)\n\n# 3D Game Shaders For Beginners\n\n## Lookup Table (LUT)\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WrPzVlW.gif\" alt=\"LUT\" title=\"LUT\">\n</p>\n\nThe lookup table or LUT shader allows you to transform the colors of your game\nusing an image editor like the [GIMP](https://www.gimp.org/).\nFrom color grading to turning day into night,\nthe LUT shader is a handy tool for tweaking the look of your game.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/NPdJNGj.png\" alt=\"Neutral LUT\" title=\"Neutral LUT\">\n</p>\n\nBefore you can get started,\nyou'll need to find a neutral LUT image.\nNeutral meaning that it leaves the fragment colors unchanged.\nThe LUT needs to be 256 pixels wide by 16 pixels tall and contain 16 blocks\nwith each block being 16 by 16 pixels.\n\nThe LUT is mapped out into 16 blocks.\nEach block has a different level of blue.\nAs you move across the blocks, from left to right, the amount of blue increases.\nYou can see the amount of blue in each block's upper-left corner.\nWithin each block,\nthe amount of red increases as you move from left to right and\nthe amount of green increases as you move from top to bottom.\nThe upper-left corner of the first block is black since every RGB channel is zero.\nThe lower-right corner of the last block is white since every RGB channel is one.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/KyxPm1r.png\" alt=\"LUT And Screenshot\" title=\"LUT And Screenshot\">\n</p>\n\nWith the neutral LUT in hand, take a screenshot of your game and open it in your image editor.\nAdd the neutral LUT as a new layer and merge it with the screenshot.\nAs you manipulate the colors of the screenshot, the LUT will be altered in the same way.\nWhen you're done editing, select only the LUT and save it as a new image.\nYou now have your new lookup table and can begin writing your shader.\n\n```c\n  // ...\n\n  vec2 texSize = textureSize(colorTexture, 0).xy;\n\n  vec4 color = texture(colorTexture, gl_FragCoord.xy / texSize);\n\n  // ...\n```\n\nThe LUT shader is a screen space technique.\nTherefore, sample the scene's color at the current fragment or screen position.\n\n```c\n  // ...\n\n  float u  =  floor(color.b * 15.0) / 15.0 * 240.0;\n        u  = (floor(color.r * 15.0) / 15.0 *  15.0) + u;\n        u /= 255.0;\n\n  float v  = ceil(color.g * 15.0);\n        v /= 15.0;\n        v  = 1.0 - v;\n\n  // ...\n```\n\nIn order to transform the current fragment's color,\nusing the LUT,\nyou'll need to map the color to two UV coordinates on the lookup table texture.\nThe first mapping (shown up above) is to the nearest left or lower bound block location and\nthe second mapping (shown below) is to the nearest right or upper bound block mapping.\nAt the end, you'll combine these two mappings to create the final color transformation.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/j2JmyQ2.png\" alt=\"RGB Channel Mapping\" title=\"RGB Channel Mapping\">\n</p>\n\nEach of the red, green, and blue channels maps to one of 16 possibilities in the LUT.\nThe blue channel maps to one of the 16 upper-left block corners.\nAfter the blue channel maps to a block,\nthe red channel maps to one of the 16 horizontal pixel positions within the block and\nthe green channel maps to one of the 16 vertical pixel positions within the block.\nThese three mappings will determine the UV coordinate you'll need to sample a color from the LUT.\n\n```c\n  // ...\n\n        u /= 255.0;\n\n        v /= 15.0;\n        v  = 1.0 - v;\n\n  // ...\n```\n\nTo calculate the final U coordinate, divide it by 255 since the LUT is 256 pixels wide and U ranges from zero to one.\nTo calculate the final V coordinate, divide it by 15 since the LUT is 16 pixels tall and V ranges from zero to one.\nYou'll also need to subtract the normalized V coordinate from one since V ranges from zero at the bottom to one at the top while\nthe green channel ranges from zero at the top to 15 at the bottom.\n\n```c\n  // ...\n\n  vec3 left = texture(lookupTableTexture, vec2(u, v)).rgb;\n\n  // ...\n```\n\nUsing the UV coordinates, sample a color from the lookup table.\nThis is the nearest left block color.\n\n```c\n  // ...\n\n  u  =  ceil(color.b * 15.0) / 15.0 * 240.0;\n  u  = (ceil(color.r * 15.0) / 15.0 *  15.0) + u;\n  u /= 255.0;\n\n  v  = 1.0 - (ceil(color.g * 15.0) / 15.0);\n\n  vec3 right = texture(lookupTableTexture, vec2(u, v)).rgb;\n\n  // ...\n```\n\nNow you'll need to calculate the UV coordinates for the nearest right block color.\nNotice how `ceil` or ceiling is being used now instead of `floor`.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/uciq7Um.png\" alt=\"Mixing\" title=\"Mixing\">\n</p>\n\n```c\n  // ...\n\n  color.r = mix(left.r, right.r, fract(color.r * 15.0));\n  color.g = mix(left.g, right.g, fract(color.g * 15.0));\n  color.b = mix(left.b, right.b, fract(color.b * 15.0));\n\n  // ...\n```\n\nNot every channel will map perfectly to one of its 16 possibilities.\nFor example, `0.5` doesn't map perfectly.\nAt the lower bound (`floor`),\nit maps to `0.4666666666666667` and at the upper bound (`ceil`),\nit maps to `0.5333333333333333`.\nCompare that with `0.4` which maps to `0.4` at the lower bound and `0.4` at the upper bound.\nFor those channels which do not map perfectly,\nyou'll need to mix the left and right sides based on where the channel falls between its lower and upper bound.\nFor `0.5`, it falls directly between them making the final color a mixture of half left and half right.\nHowever,\nfor `0.132` the mixture will be 98% right and 2% left since the fractional part of `0.123` times `15.0` is `0.98`.\n\n```c\n  // ...\n\n  fragColor = color;\n\n  // ...\n```\n\nSet the fragment color to the final mix and you're done.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [lookup-table.frag](../demonstration/shaders/fragment/lookup-table.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](film-grain.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](gamma-correction.md)\n"
  },
  {
    "path": "sections/motion-blur.md",
    "content": "[:arrow_backward:](ssao.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](chromatic-aberration.md)\n\n# 3D Game Shaders For Beginners\n\n## Motion Blur\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/eTnhpLr.gif\" alt=\"Motion Blur\" title=\"Motion Blur\">\n</p>\n\nTo really sell the illusion of speed, you can do no better than motion blur.\nFrom high speed car chases to moving at warp speed,\nmotion blur greatly improves the look and feel of fast moving objects.\n\nThere are a few ways to implement motion blur as a screen space technique.\nThe less involved implementation will only blur the scene in relation to the camera's movements\nwhile the more involved version will blur any moving objects even with the camera remaining still.\nThe less involved technique is described below but the principle is the same.\n\n### Textures\n\n```c\nuniform sampler2D positionTexture;\nuniform sampler2D colorTexture;\n\n// ...\n```\n\nThe input textures needed are the vertex positions in view space and the scene's colors.\nRefer back to [SSAO](ssao.md#vertex-positions) for acquiring the vertex positions.\n\n### Matrices\n\n```c\n// ...\n\nuniform mat4 previousViewWorldMat;\nuniform mat4 worldViewMat;\nuniform mat4 lensProjection;\n\n// ...\n```\n\nThe motion blur technique determines the blur direction by comparing\nthe previous frame's vertex positions with the current frame's vertex positions.\nTo do this, you'll need the previous frame's view-to-world matrix,\nthe current frame's world-to-view matrix,\nand the camera lens' projection matrix.\n\n### Parameters\n\n```c\n// ...\n\nuniform vec2 parameters;\n\n// ...\n\nvoid main() {\n  int   size       = int(parameters.x);\n  float separation =     parameters.y;\n\n// ...\n```\n\nThe adjustable parameters are `size` and `separation`.\n`size` controls how many samples are taken along the blur direction.\nIncreasing `size` increases the amount of blur at the cost of performance.\n`separation` controls how spread out the samples are along the blur direction.\nIncreasing `separation` increases the amount of blur at the cost of accuracy.\n\n### Blur Direction\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 position1 = texture(positionTexture, texCoord);\n  vec4 position0 = worldViewMat * previousViewWorldMat * position1;\n\n  // ...\n```\n\nTo determine which way to blur this fragment,\nyou'll need to know where things were last frame and where things are this frame.\nTo figure out where things are now,\nsample the current vertex position.\nTo figure out where things were last frame,\ntransform the current position from view space to world space,\nusing the previous frame's view-to-world matrix,\nand then transform it back to view space from world space using this frame's world-to-view matrix.\nThis transformed position is this fragment's previous interpolated vertex position.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/oQqdxM9.gif\" alt=\"Position Projection\" title=\"Position Projection\">\n</p>\n\n```c\n  // ...\n\n  position0      = lensProjection * position0;\n  position0.xyz /= position0.w;\n  position0.xy   = position0.xy * 0.5 + 0.5;\n\n  position1      = lensProjection * position1;\n  position1.xyz /= position1.w;\n  position1.xy   = position1.xy * 0.5 + 0.5;\n\n  // ...\n```\n\nNow that you have the current and previous positions,\ntransform them to screen space.\nWith the positions in screen space,\nyou can trace out the 2D direction you'll need to blur the onscreen image.\n\n```c\n  // ...\n\n  //   position1.xy = position0.xy + direction;\n  vec2 direction    = position1.xy - position0.xy;\n\n  if (length(direction) <= 0.0) { return; }\n\n  // ...\n```\n\nThe blur direction goes from the previous position to the current position.\n\n### Blurring\n\n```c\n  // ...\n\n  fragColor = texture(colorTexture, texCoord);\n\n  // ...\n```\n\nSample the current fragment's color.\nThis will be the first of the colors blurred together.\n\n```c\n  // ...\n\n  direction.xy *= separation;\n\n  // ...\n```\n\nMultiply the direction vector by the separation.\n\n```c\n  // ...\n\n  vec2  forward  = texCoord;\n  vec2  backward = texCoord;\n\n  // ...\n```\n\nFor a more seamless blur,\nsample in the direction of the blur and in the opposite direction of the blur.\nFor now, set the two vectors to the fragment's UV coordinate.\n\n```c\n  // ...\n\n  float count = 1.0;\n\n  // ...\n```\n\n`count` is used to average all of the samples taken.\nIt starts at one since you've already sampled the current fragment's color.\n\n```c\n  // ...\n\n  for (int i = 0; i < size; ++i) {\n    forward  += direction;\n    backward -= direction;\n\n    fragColor +=\n      texture\n        ( colorTexture\n        , forward\n        );\n    fragColor +=\n      texture\n        ( colorTexture\n        , backward\n        );\n\n    count += 2.0;\n  }\n\n  // ...\n```\n\nSample the screen's colors both in the forward and backward direction of the blur.\nBe sure to add these samples together as you travel along.\n\n```c\n  // ...\n\n  fragColor /= count;\n}\n```\n\nThe final fragment color is the average color of the samples taken.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [motion-blur.frag](../demonstration/shaders/fragment/motion-blur.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](ssao.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](chromatic-aberration.md)\n"
  },
  {
    "path": "sections/normal-mapping.md",
    "content": "[:arrow_backward:](cel-shading.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](deferred-rendering.md)\n\n# 3D Game Shaders For Beginners\n\n## Normal Mapping\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/M4eHo9I.gif\" alt=\"Normal Mapping\" title=\"Normal Mapping\">\n</p>\n\nNormal mapping allows you to add surface details without adding any geometry.\nTypically, in a modeling program like Blender, you create a high poly and a low poly version of your mesh.\nYou take the vertex normals from the high poly mesh and bake them into a texture.\nThis texture is the normal map.\nThen inside the fragment shader, you replace the low poly mesh's vertex normals with the\nhigh poly mesh's normals you baked into the normal map.\nNow when you light your mesh, it will appear to have more polygons than it really has.\nThis will keep your FPS high while at the same time retain most of the details from the high poly version.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/nSY9AW4.gif\" alt=\"From high to low poly with normal mapping.\" title=\"From high to low poly with normal mapping.\">\n</p>\n\nHere you see the progression from the high poly model to the low poly model to the low poly model with the normal map applied.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/jvkRPE7.gif\" alt=\"Normal Map Illusion\" title=\"Normal Map Illusion\">\n</p>\n\nKeep in mind though, normal mapping is only an illusion.\nAfter a certain angle, the surface will look flat again.\n\n### Vertex\n\n```c\n// ...\n\nuniform mat3 p3d_NormalMatrix;\n\n// ...\n\nin vec3 p3d_Normal;\n\n// ...\n\nin vec3 p3d_Binormal;\nin vec3 p3d_Tangent;\n\n  // ...\n\n  vertexNormal = normalize(p3d_NormalMatrix * p3d_Normal);\n  binormal     = normalize(p3d_NormalMatrix * p3d_Binormal);\n  tangent      = normalize(p3d_NormalMatrix * p3d_Tangent);\n\n  // ...\n```\n\nStarting in the vertex shader,\nyou'll need to output to the fragment shader the normal vector, binormal vector, and the tangent vector.\nThese vectors are used, in the fragment shader, to transform the normal map normal from tangent space to view space.\n\n`p3d_NormalMatrix` transforms the vertex normal, binormal, and tangent vectors to view space.\nRemember that in view space, all of the coordinates are relative to the camera's position.\n\n<blockquote>\n[p3d_NormalMatrix] is the upper 3x3 of the inverse transpose of the ModelViewMatrix.\nIt is used to transform the normal vector into view-space coordinates.\n<br>\n<br>\n<footer>\n<a href=\"http://www.panda3d.org/manual/?title=List_of_GLSL_Shader_Inputs\">Source</a>\n</footer>\n</blockquote>\n\n```c\n// ...\n\nin vec2 p3d_MultiTexCoord0;\n\n// ...\n\nout vec2 normalCoord;\n\n  // ...\n\n  normalCoord   = p3d_MultiTexCoord0;\n\n  // ...\n```\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tLIA6Hu.gif\" alt=\"Normal Maps\" title=\"Normal Maps\">\n</p>\n\nYou'll also need to output, to the fragment shader, the UV coordinates for the normal map.\n\n### Fragment\n\nRecall that the vertex normal was used to calculate the lighting.\nHowever, the normal map provides us with different normals to use when calculating the lighting.\nIn the fragment shader, you need to swap out the vertex normals for the normals found in the normal map.\n\n```c\n// ...\n\nuniform sampler2D p3d_Texture1;\n\n// ...\n\nin vec2 normalCoord;\n\n  // ...\n\n  /* Find */\n  vec4 normalTex   = texture(p3d_Texture1, normalCoord);\n\n  // ...\n```\n\nUsing the normal map coordinates the vertex shader sent, pull out the normal from the normal map.\n\n```c\n  // ...\n\n  vec3 normal;\n\n    // ...\n\n    /* Unpack */\n    normal =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n\n    // ...\n```\n\nEarlier I showed how the normals are mapped to colors to create the normal map.\nNow this process needs to be reversed so you can get back the original normals that were baked into the map.\n\n```c\n(r, g, b) =\n  ( r * 2 - 1\n  , g * 2 - 1\n  , b * 2 - 1\n  ) =\n    (x, y, z)\n```\n\nHere's the process for unpacking the normals from the normal map.\n\n```c\n    // ...\n\n    /* Transform */\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normal\n        );\n\n    // ...\n```\n\nThe normals you get back from the normal map are typically in tangent space.\nThey could be in another space, however.\nFor example, Blender allows you to bake the normals in tangent, object, world, or camera space.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EzHJPd4.gif\" alt=\"Replacing the vertex normals with the normal map normals.\" title=\"Replacing the vertex normals with the normal map normals.\">\n</p>\n\nTo take the normal map normal from tangent space to view pace,\nconstruct a three by three matrix using the tangent, binormal, and vertex normal vectors.\nMultiply the normal by this matrix and be sure to normalize it.\n\nAt this point, you're done.\nThe rest of the lighting calculations are the same.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](cel-shading.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](deferred-rendering.md)\n"
  },
  {
    "path": "sections/outlining.md",
    "content": "[:arrow_backward:](flow-mapping.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](depth-of-field.md)\n\n# 3D Game Shaders For Beginners\n\n## Outlining\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/EKaZRkR.gif\" alt=\"Outlined Scene\" title=\"Outlined Scene\">\n</p>\n\nOutlining your scene's geometry can give your game a distinctive look,\nreminiscent of comic books and cartoons.\n\n### Discontinuities\n\nThe process of outlining is the process of finding and labeling discontinuities or differences.\nEvery time you find what you consider a significant difference,\nyou mark it with your line color.\nAs you go about labeling or coloring in the differences, outlines or edges will start to form.\n\nWhere you choose to search for the discontinuities is up to you.\nIt could be the diffuse colors in your scene,\nthe normals of your models,\nthe depth buffer,\nor some other scene related data.\n\nThe demo uses the interpolated vertex positions to render the outlines.\nHowever,\na less straightforward but more typical way is to use both the\nscene's normals and depth buffer values to construct the outlines.\n\n### Vertex Positions\n\n```c\n// ...\n\nuniform sampler2D positionTexture;\n\n// ...\n```\n\nLike SSAO, you'll need the vertex positions in view space.\nReferrer back to [SSAO](ssao.md#vertex-positions) for details.\n\n### Scene Colors\n\n```c\n// ...\n\nuniform sampler2D colorTexture;\n\n// ...\n```\n\nThe demo darkens the colors of the scene where there's an outline.\nThis tends to look nicer than a constant color since it provides\nsome color variation to the edges.\n\n### Parameters\n\n```c\n  // ...\n\n  float minSeparation = 1.0;\n  float maxSeparation = 3.0;\n  float minDistance   = 0.5;\n  float maxDistance   = 2.0;\n  int   size          = 1;\n  vec3 colorModifier  = vec3(0.324, 0.063, 0.099);\n\n  // ...\n```\n\nThe min and max separation parameters control the thickness of the outline\ndepending on the fragment's distance from the camera or depth.\nThe min and max distance control the significance of any changes found.\nThe `size` parameter controls the constant thickness of the line no matter the fragment's position.\nThe outline color is based on `colorModifier` and the current fragment's color.\n\n### Fragment Position\n\n```c\n  // ...\n\n  vec2 texSize   = textureSize(colorTexture, 0).xy;\n  vec2 fragCoord = gl_FragCoord.xy;\n  vec2 texCoord  = fragCoord / texSize;\n\n  vec4 position = texture(positionTexture, texCoord);\n\n  // ...\n```\n\nSample the position texture for the current fragment's position in the scene.\nRecall that the position texture is just a screen shaped quad making the UV coordinate\nthe current fragment's screen coordinate divided by the dimensions of the screen.\n\n### Fragment Depth\n\n```c\n  // ...\n\n  float depth =\n    clamp\n      (   1.0\n        - ( (far - position.y)\n          / (far - near)\n        )\n      , 0.0\n      , 1.0\n      );\n\n  float separation = mix(maxSeparation, minSeparation, depth);\n\n  // ...\n```\n\nThe fragment's depth ranges from zero to one.\nWhen the fragment's view-space y coordinate matches the far clipping plane,\nthe depth is one.\nWhen the fragment's view-space y coordinate matches the near clipping plane,\nthe depth is zero.\nIn other words,\nthe depth ranges from zero at the near clipping plane all the way up to one at the far clipping plane.\n\n```c\n  // ...\n\n  float separation = mix(maxSeparation, minSeparation, depth);\n\n  // ...\n```\n\nConverting the position to a depth value isn't necessary\nbut it allows you to vary the thickness of the outline based on how far\naway the fragment is from the camera.\nFar away fragments get a thinner line while nearer fragments get a thicker outline.\nThis tends to look nicer than a constant thickness since it gives depth to the outline.\n\n### Finding The Discontinuities\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/xAMRGhn.gif\" alt=\"Edge Finding\" title=\"Edge Finding\">\n</p>\n\n\n```c\n  // ...\n\n  float mx = 0.0;\n\n  for (int i = -size; i <= size; ++i) {\n    for (int j = -size; j <= size; ++j) {\n      // ...\n    }\n  }\n\n  // ...\n```\n\nNow that you have the current fragment's position,\nloop through an i by j grid or window surrounding the current fragment.\n\n\n```c\n      // ...\n\n      texCoord =\n          ( fragCoord\n          + (vec2(i, j) * separation)\n          )\n        / texSize;\n\n      vec4 positionTemp =\n        texture\n          ( positionTexture\n          , texCoord\n          );\n\n      mx = max(mx, abs(position.y - positionTemp.y));\n\n      // ...\n```\n\nWith each iteration,\nfind the biggest distance between this fragment's\nand the surrounding fragments' positions.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/idDZr62.png\" alt=\"smoothstep\" title=\"smoothstep\">\n</p>\n\n```c\n  // ...\n\n  float diff = smoothstep(minDistance, maxDistance, mx);\n\n  // ...\n```\n\nCalculate the significance of any difference discovered using the `minDistance`, `maxDistance`, and `smoothstep`.\n`smoothstep` returns values from zero to one.\nThe `minDistance` is the left-most edge.\nAny difference less than the minimum distance will be zero.\nThe `maxDistance` is the right-most edge.\nAny difference greater than the maximum distance will be one.\nFor distances between the edges,\nthe difference will be between zero and one.\nThese values are interpolated along a s-shaped curve.\n\n### Line Color\n\n```c\n  // ...\n\n  vec3 lineColor = texture(colorTexture, texCoord).rgb * colorModifier;\n\n  // ...\n```\n\nThe line color is the current fragment color either darkened or lightened.\n\n### Fragment Color\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/yWNdPZe.gif\" alt=\"Outlines\" title=\"Outlines\">\n</p>\n\n```c\n  // ...\n\n  fragColor.rgb = vec4(lineColor, diff);\n\n  // ...\n```\n\nThe fragment's RGB color is the `lineColor` and its alpha channel is `diff`.\n\n### Sketchy\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FRBkSm1.gif\" alt=\"Sketchy Outline\" title=\"Sketchy Outline\">\n</p>\n\nFor a sketchy outline, you can distort the UV coordinates used to sample the position vectors.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/qgZNtnN.png\" alt=\"Outline Noise\" title=\"Outline Noise\">\n</p>\n\n```c\n// ...\n\nuniform sampler2D noiseTexture;\n\n// ...\n```\n\nStart by creating a RGB noise texture.\nA good size is either 128 by 128 or 512 by 512.\nBe sure to blur it and make it tileable.\nThis will produce a nice wavy, inky outline.\n\n```c\n  // ...\n\n  float noiseScale = 10.0;\n\n  // ...\n```\n\nThe `noiseScale` parameter controls how distorted the outline is.\nThe bigger the `noiseScale`, the sketchier the line.\n\n```c\n  // ...\n\n  vec2 fragCoord = gl_FragCoord.xy;\n\n  vec2 noise  = texture(noiseTexture, fragCoord / textureSize(noiseTexture, 0).xy).rb;\n       noise  = noise * 2.0 - 1.0;\n       noise *= noiseScale;\n\n  // ...\n```\n\nSample the noise texture using the current screen/fragment position and the size of the noise texture.\nSince you're distorting the UV coordinates used to sample the position vectors,\nyou'll only need two of the three color channels.\nMap the two color channels from `[0, 1]` to `[-1, 1]`.\nFinally, scale the noise by the scale chosen earlier.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(colorTexture, 0).xy;\n  vec2 texCoord = (fragCoord - noise) / texSize;\n\n  vec4 position = texture(positionTexture, texCoord);\n\n  // ...\n```\n\nWhen sampling the current position,\nsubtract the noise vector from the current fragment's coordinates.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/wk43ybP.png\" alt=\"Squiggly Outline\" title=\"Squiggly Outline\">\n</p>\n\nYou could instead add it to the current fragment's coordinates\nwhich will create more of a squiggly line that loosely follows the geometry.\n\n```c\n  // ...\n\n      texCoord =\n          (vec2(i, j) * separation + fragCoord + noise)\n        / texSize;\n\n      // ...\n\n      vec4 positionTemp =\n        texture\n          ( positionTexture\n          , texCoord\n          );\n\n      // ...\n```\n\nWhen sampling the surrounding positions inside the loop,\nadd the noise vector to the current fragment's coordinates.\nThe rest of the calculations are the same.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [outline.frag](../demonstration/shaders/fragment/outline.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](flow-mapping.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](depth-of-field.md)\n"
  },
  {
    "path": "sections/pixelization.md",
    "content": "[:arrow_backward:](posterization.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](sharpen.md)\n\n# 3D Game Shaders For Beginners\n\n## Pixelization\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IbnyYZN.gif\" alt=\"Pixelization\" title=\"Pixelization\">\n</p>\n\nPixelizing your 3D game can give it a interesting look and\npossibly save you time by not having to create all of the pixel art by hand.\nCombine it with the posterization for a true retro look.\n\n```c\n  // ...\n\n  int pixelSize = 5;\n\n  // ...\n```\n\nFeel free to adjust the pixel size.\nThe bigger the pixel size, the blockier the image will be.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/WF5MmM0.gif\" alt=\"Pixelization Process\" title=\"Pixelization Process\">\n</p>\n\n```c\n  // ...\n\n  float x = int(gl_FragCoord.x) % pixelSize;\n  float y = int(gl_FragCoord.y) % pixelSize;\n\n  x = floor(pixelSize / 2.0) - x;\n  y = floor(pixelSize / 2.0) - y;\n\n  x = gl_FragCoord.x + x;\n  y = gl_FragCoord.y + y;\n\n  // ...\n```\n\nThe technique works by mapping each fragment to the center of its closest, non-overlapping\npixel-sized window.\nThese windows are laid out in a grid over the input texture.\nThe center-of-the-window fragments determine the color for the other fragments in their window.\n\n```c\n    // ...\n\n    fragColor = texture(colorTexture, vec2(x, y) / texSize);\n\n    // ...\n```\n\nOnce you have determined the correct fragment coordinate to use,\npull its color from the input texture and assign that to the fragment color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [pixelize.frag](../demonstration/shaders/fragment/pixelize.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](posterization.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](sharpen.md)\n"
  },
  {
    "path": "sections/posterization.md",
    "content": "[:arrow_backward:](depth-of-field.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](pixelization.md)\n\n# 3D Game Shaders For Beginners\n\n## Posterization\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/hpP5G9z.gif\" alt=\"Posterization\" title=\"Posterization\">\n</p>\n\nPosterization or color quantization is the process of reducing the number of unique colors in an image.\nYou can use this shader to give your game a comic book or retro look.\nCombine it with [outlining](outlining.md) for a full-on cartoon art style.\n\nThere are various different ways to implement posterization.\nThis method works directly with the greyscale values and indirectly with the RGB values of the image.\nFor each fragment, it maps the RGB color to a greyscale value.\nThis greyscale value is then mapped to both its lower and upper level value.\nThe closest level to the original greyscale value is then mapped back to an RGB value\nThis new RGB value becomes the fragment color.\nI find this method produces nicer results than the more typical methods you'll find.\n\n```c\n  // ...\n\n  float levels = 10;\n\n  // ...\n```\n\nThe `levels` parameter controls how many discrete bands or steps there are.\nThis will break up the continuous values from zero to one into chunks.\nWith four levels, `0.0` to `1.0` becomes `0.0`, `0.25`, `0.5`, `0.75`, and `1.0`.\n\n\n```c\n  // ...\n\n  fragColor = texture(posterizeTexture, texCoord);\n\n  // ...\n```\n\nSample the current fragment's color.\n\n```c\n  // ...\n\n  float greyscale = max(fragColor.r, max(fragColor.g, fragColor.b));\n\n  // ...\n```\n\nMap the RGB values to a greyscale value.\nIn this instance, the greyscale value is the maximum value of the R, G, and B values.\n\n```c\n  // ...\n\n  float lower     = floor(greyscale * levels) / levels;\n  float lowerDiff = abs(greyscale - lower);\n\n  // ...\n```\n\nMap the greyscale value to its lower level and\nthen calculate the difference between its lower level and itself.\nFor example,\nif the greyscale value is `0.87` and there are four levels, its lower level is `0.75` and the difference is `0.12`.\n\n\n```c\n  // ...\n\n  float upper     = ceil(greyscale * levels) / levels;\n  float upperDiff = abs(upper - greyscale);\n\n  // ...\n```\n\nNow calculate the upper level and the difference.\nKeeping with the example up above, the upper level is `1.0` and the difference is `0.13`.\n\n```c\n  // ...\n\n  float level      = lowerDiff <= upperDiff ? lower : upper;\n  float adjustment = level / greyscale;\n\n  // ...\n```\n\nThe closest level is used to calculate the adjustment.\nThe adjustment is the ratio between the quantized and unquantized greyscale value.\nThis adjustment is used to map the quantized greyscale value back to an RGB value.\n\n\n```c\n  // ...\n\n  fragColor.rgb * adjustment;\n\n  // ...\n```\n\nAfter multiplying `rgb` by the adjustment, `max(r, max(g, b))` will now equal the quantized greyscale value.\nThis maps the quantized greyscale value back to a red, green, and blue vector.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [posterize.frag](../demonstration/shaders/fragment/posterize.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](depth-of-field.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](pixelization.md)\n"
  },
  {
    "path": "sections/reference-frames.md",
    "content": "[:arrow_backward:](running-the-demo.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](glsl.md)\n\n# 3D Game Shaders For Beginners\n\n## Reference Frames\n\nBefore you write any shaders, you should be familiar with the following frames of reference or coordinate systems.\nAll of them boil down to what origin `(0, 0, 0)` are these coordinates currently relative to?\nOnce you know that, you can then transform them, via some matrix, to some other vector space if need be.\nTypically, when the output of some shader looks wrong, it's because of some coordinate system mix up.\n\n### Model\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/8xptajU.gif\" alt=\"Model Space\" title=\"Model Space\">\n</p>\n\nThe model or object coordinate system is relative to the origin of the model.\nThis is typically set to the center of the model's geometry in a modeling program like Blender.\n\n### World\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fHl4ohX.gif\" alt=\"World Space\" title=\"World Space\">\n</p>\n\nThe world space is relative to the origin of the scene/level/universe that you've created.\n\n### View\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3b4SGGH.gif\" alt=\"View Space\" title=\"View Space\">\n</p>\n\nThe view or eye coordinate space is relative to the position of the active camera.\n\n### Clip\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iSEWS9Y.png\" alt=\"Clip Space\" title=\"Clip Space\">\n</p>\n\nThe clip space is relative to the center of the camera's film.\nAll coordinates are now homogeneous, ranging from negative one to one `(-1, 1)`.\nX and y are parallel with the camera's film and the z coordinate is the depth.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MhgmOLv.gif\" alt=\"Frustum\" title=\"Frustum\">\n</p>\n\nAny vertex not within the bounds of the camera's frustum or view volume is clipped or discarded.\nYou can see this happening with the cube towards the back, clipped by the camera's far plane, and the cube off to the side.\n\n### Screen\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bHHrjOl.png\" alt=\"Screen Space\" title=\"Screen Space\">\n</p>\n\nThe screen space is (typically) relative to the lower left corner of the screen.\nX goes from zero to the screen width.\nY goes from zero to the screen height.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](running-the-demo.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](glsl.md)\n"
  },
  {
    "path": "sections/render-to-texture.md",
    "content": "[:arrow_backward:](glsl.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](texturing.md)\n\n# 3D Game Shaders For Beginners\n\n## Render To Texture\n\nInstead of rendering/drawing/painting directly to the screen, the example code uses a technique called \"render to texture\".\nIn order to render to a texture, you'll need to set up a framebuffer and bind a texture to it.\nMultiple textures can be bound to a single framebuffer.\n\nThe textures bound to the framebuffer hold the vector(s) returned by the fragment shader.\nTypically these vectors are color vectors `(r, g, b, a)` but they could also be position or normal vectors `(x, y, z, w)`.\nFor each bound texture, the fragment shader can output a different vector.\nFor example you could output a vertex's position and normal in a single pass.\n\nMost of the example code dealing with Panda3D involves setting up\n[framebuffer textures](https://www.panda3d.org/manual/?title=Render-to-Texture_and_Image_Postprocessing).\nTo keep things straightforward, nearly all of the fragment shaders in the example code have only one output.\nHowever, you'll want to output as much as you can each render pass to keep your frames per second (FPS) high.\n\nThere are two framebuffer texture setups found in the example code.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/t3iLKhx.gif\" alt=\"The first framebuffer texture setup.\" title=\"The first framebuffer texture setup.\">\n</p>\n\nThe first setup renders the mill scene into a framebuffer texture using a variety of vertex and fragment shaders.\nThis setup will go through each of the mill scene's vertexes and corresponding fragments.\n\nIn this setup, the example code performs the following.\n\n- Stores geometry data (like vertex position or normal) for later use.\n- Stores material data (like the diffuse color) for later use.\n- UV maps the various textures (diffuse, normal, shadow, etc.).\n- Calculates the ambient, diffuse, specular, and emission lighting.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/o8H6cTy.png\" alt=\"The second framebuffer texture setup.\" title=\"The second framebuffer texture setup.\">\n</p>\n\nThe second setup is an orthographic camera pointed at a screen-shaped rectangle.\nThis setup will go through just the four vertexes and their corresponding fragments.\n\nIn this second setup, the example code performs the following.\n\n- Manipulates the output of another framebuffer texture.\n- Combines various framebuffer textures into one.\n\nI like to think of this second setup as using layers in GIMP, Krita, or Inkscape.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/L6Hwuxa.gif\" alt=\"Tabbing Through Framebuffer Textures\" title=\"Tabbing Through Framebuffer Textures\">\n</p>\n\nIn the example code, you can see the output of a particular framebuffer texture\nby using the <kbd>Tab</kbd> key or the <kbd>Shift</kbd>+<kbd>Tab</kbd> keys.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](glsl.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](texturing.md)\n"
  },
  {
    "path": "sections/rim-lighting.md",
    "content": "[:arrow_backward:](fresnel-factor.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](cel-shading.md)\n\n# 3D Game Shaders For Beginners\n\n## Rim Lighting\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/3erauzN.gif\" alt=\"Rim Lighting\" title=\"Rim Lighting\">\n</p>\n\nTaking inspiration from the [fresnel factor](fresnel-factor.md),\nrim lighting targets the rim or silhouette of an object.\nWhen combined with [cel shading](cel-shading.md) and [outlining](outlining.md),\nit can really complete that cartoon look.\nYou can also use it to highlight objects in the game,\nmaking it easier for players to navigate and accomplish tasks.\n\n```c\n  // ...\n\n  vec3 eye = normalize(-vertexPosition.xyz);\n\n  // ...\n```\n\nAs it was for the fresnel factor,\nyou'll need the eye vector.\nIf your vertex positions are in view space,\nthe eye vector is the negation of the vertex position.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/mMsQFbE.gif\" alt=\"Rim Light\" title=\"Rim Light\">\n</p>\n\n```c\n  // ...\n\n  float rimLightIntensity = dot(eye, normal);\n        rimLightIntensity = 1.0 - rimLightIntensity;\n        rimLightIntensity = max(0.0, rimLightIntensity);\n\n  // ...\n```\n\nThe Intensity of the rim light ranges from zero to one.\nWhen the eye and normal vector point in the same direction,\nthe rim light intensity is zero.\nAs the two vectors start to point in different directions,\nthe rim light intensity increases\nuntil it eventually reaches one when the eye and normal become orthogonal or perpendicular to one another.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AAFI8p1.gif\" alt=\"Rim Light Power\" title=\"Rim Light Power\">\n</p>\n\n```c\n  // ...\n\n  rimLightIntensity = pow(rimLightIntensity, rimLightPower);\n\n  // ...\n```\n\nYou can control the falloff of the rim light using the power function.\n\n```c\n  // ...\n\n  rimLightIntensity = smoothstep(0.3, 0.4, rimLightIntensity)\n\n  // ...\n```\n\n`step` or `smoothstep` can also be used to control the falloff.\nThis tends to look better when using [cel shading](cel-shading.md).\nYou'll learn more about these functions in later sections.\n\n```c\n  // ...\n\n  vec4 rimLight   = rimLightIntensity * diffuse;\n       rimLight.a = diffuse.a;\n\n  // ...\n```\n\nWhat color you use for the rim light is up to you.\nThe demo code multiplies the diffuse light by the `rimLightIntensity`.\nThis will highlight the silhouette without overexposing it\nand without lighting any shadowed fragments.\n\n```c\n  // ...\n\n  vec4 outputColor     = vec4(0.0);\n       outputColor.a   = diffuseColor.a;\n       outputColor.rgb =\n           ambient.rgb\n        +  diffuse.rgb\n        + specular.rgb\n        + rimLight.rgb\n        + emission.rgb;\n\n  // ...\n```\n\nAfter you've calculated the rim light,\nadd it to the ambient, diffuse, specular, and emission lights.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n\n## Copyright\n\n(C) 2020 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](fresnel-factor.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](cel-shading.md)\n"
  },
  {
    "path": "sections/running-the-demo.md",
    "content": "[:arrow_backward:](building-the-demo.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](reference-frames.md)\n\n# 3D Game Shaders For Beginners\n\n## Running The Demo\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/y5XcReP.gif\" alt=\"Running The Demo\" title=\"Running The Demo\">\n</p>\n\nAfter you've built the example code, you can now run the executable or demo.\n\n```bash\n./3d-game-shaders-for-beginners\n```\n\nHere's how you run it on Linux or Mac.\n\n```bash\n3d-game-shaders-for-beginners.exe\n```\n\nHere's how you run it on Windows.\n\n### Demo Controls\n\nThe demo comes with both keyboard and mouse controls to move the camera around,\ntoggle on and off the different effects,\nadjust the fog,\nand view the various different framebuffer textures.\n\n#### Mouse\n\nYou can rotate the scene around by holding down the <kbd>Left Mouse</kbd> button and dragging.\nHold down the <kbd>Right Mouse</kbd> button and drag to move up, down, left, and/or right.\nTo zoom in, roll the <kbd>Mouse Wheel</kbd> forward.\nTo zoom out, roll the <kbd>Mouse Wheel</kbd> backward.\n\nYou can also change the focus point using the mouse.\nTo change the focus point,\nclick anywhere on the scene using the <kbd>Middle Mouse</kbd> button.\n\n#### Keyboard\n\n- <kbd>w</kbd> to rotate the scene down.\n- <kbd>a</kbd> to rotate the scene clockwise.\n- <kbd>s</kbd> to rotate the scene up.\n- <kbd>d</kbd> to rotate the scene counterclockwise.\n- <kbd>z</kbd> to zoom in to the scene.\n- <kbd>x</kbd> to zoom out of the scene.\n- <kbd>⬅</kbd> to move left.\n- <kbd>➡</kbd> to move right.\n- <kbd>⬆</kbd> to move up.\n- <kbd>⬇</kbd> to move down.\n\n<p></p>\n\n- <kbd>1</kbd> to show midday.\n- <kbd>2</kbd> to show midnight.\n\n<p></p>\n\n- <kbd>Delete</kbd> to toggle the sound.\n- <kbd>3</kbd> to toggle fresnel.\n- <kbd>4</kbd> to toggle rim lighting.\n- <kbd>5</kbd> to toggle particles.\n- <kbd>6</kbd> to toggle motion blur.\n- <kbd>7</kbd> to toggle Kuwahara filtering.\n- <kbd>8</kbd> to toggle cel shading.\n- <kbd>9</kbd> to toggle lookup table processing.\n- <kbd>0</kbd> to toggle between Phong and Blinn-Phong.\n- <kbd>y</kbd> to toggle SSAO.\n- <kbd>u</kbd> to toggle outlining.\n- <kbd>i</kbd> to toggle bloom.\n- <kbd>o</kbd> to toggle normal mapping.\n- <kbd>p</kbd> to toggle fog.\n- <kbd>h</kbd> to toggle depth of field.\n- <kbd>j</kbd> to toggle posterization.\n- <kbd>k</kbd> to toggle pixelization.\n- <kbd>l</kbd> to toggle sharpen.\n- <kbd>n</kbd> to toggle film grain.\n- <kbd>m</kbd> to toggle screen space reflection.\n- <kbd>,</kbd> to toggle screen space refraction.\n- <kbd>.</kbd> to toggle flow mapping.\n- <kbd>/</kbd> to toggle the sun animation.\n- <kbd>\\\\</kbd> to toggle chromatic aberration.\n\n<p></p>\n\n- <kbd>r</kbd> to reset the scene.\n\n<p></p>\n\n- <kbd>\\[</kbd> to decrease the fog near distance.\n- <kbd>Shift</kbd>+<kbd>\\[</kbd> to increase the fog near distance.\n- <kbd>]</kbd> to increase the fog far distance.\n- <kbd>Shift</kbd>+<kbd>]</kbd> to decrease the fog far distance.\n\n<p></p>\n\n- <kbd>Shift</kbd>+<kbd>-</kbd> to decrease the amount of foam.\n- <kbd>-</kbd> to increase the amount of foam.\n\n<p></p>\n\n- <kbd>Shift</kbd>+<kbd>=</kbd> to decrease the relative index of refraction.\n- <kbd>=</kbd> to increase the relative index of refraction.\n\n<p></p>\n\n- <kbd>Tab</kbd> to move forward through the framebuffer textures.\n- <kbd>Shift</kbd>+<kbd>Tab</kbd> to move backward through the framebuffer textures.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](building-the-demo.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](reference-frames.md)\n"
  },
  {
    "path": "sections/screen-space-reflection.md",
    "content": "[:arrow_backward:](chromatic-aberration.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](screen-space-refraction.md)\n\n# 3D Game Shaders For Beginners\n\n## Screen Space Reflection (SSR)\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AYG4tvg.gif\" alt=\"Screen Space Reflections\" title=\"Screen Space Reflections\">\n</p>\n\nAdding reflections can really ground your scene.\nWet and shiny objects spring to life as nothing makes\nsomething look wet or shiny quite like reflections.\nWith reflections, you can really sell the illusion of water and metallic objects.\n\nIn the [lighting](lighting.md) section, you simulated the reflected, mirror-like image of the light source.\nThis was the process of rendering the specular reflection.\nRecall that the specular light was computed using the reflected light direction.\nSimilarly, using screen space reflection or SSR, you can simulate the reflection of\nother objects in the scene instead of just the light source.\nInstead of the light ray coming from the source and bouncing off into the camera,\nthe light ray comes from some object in the scene and bounces off into the camera.\n\nSSR works by reflecting the screen image onto itself using only itself.\nCompare this to cube mapping which uses six screens or textures.\nIn cube mapping, you reflect a ray from some point in your scene\nto some point on the inside of a cube surrounding your scene.\nIn SSR, you reflect a ray from some point on your screen to some other point on your screen.\nBy reflecting your screen onto itself, you can create the illusion of reflection.\nThis illusion holds for the most part but SSR does fail in some cases as you'll see.\n\n### Ray Marching\n\nScreen space reflection uses a technique known as ray marching to determine the reflection for each fragment.\nRay marching is the process of iteratively extending or contracting the length or magnitude of some vector\nin order to probe or sample some space for information.\nThe ray in screen space reflection is the position vector reflected about the normal.\n\nIntuitively, a light ray hits some point in the scene,\nbounces off,\ntravels in the opposite direction of the reflected position vector,\nbounces off the current fragment,\ntravels in the opposite direction of the position vector,\nand hits the camera lens allowing you to see the color of some point in the scene reflected in the current fragment.\nSSR is the process of tracing the light ray's path in reverse.\nIt tries to find the reflected point the light ray bounced off of and hit the current fragment.\nWith each iteration,\nthe algorithm samples the scene's positions or depths,\nalong the reflection ray,\nasking each time if the ray intersected with the scene's geometry.\nIf there is an intersection,\nthat position in the scene is a potential candidate for being reflected by the current fragment.\n\nIdeally there would be some analytical method for determining the first intersection point exactly.\nThis first intersection point is the only valid point to reflect in the current fragment.\nInstead, this method is more like a game of battleship.\nYou can't see the intersections (if there are any) so you start at the base of the reflection ray and call out coordinates\nas you travel in the direction of the reflection.\nWith each call, you get back an answer of whether or not you hit something.\nIf you do hit something,\nyou try points around that area hoping to find the exact point of intersection.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/wnAC7NI.gif\" alt=\"SSR Ray Marching\" title=\"SSR Ray Marching\">\n</p>\n\nHere you see ray marching being used to calculate each fragment's reflected point.\nThe vertex normal is the bright green arrow,\nthe position vector is the bright blue arrow,\nand the bright red vector is the reflection ray marching through view space.\n\n### Vertex Positions\n\nLike SSAO, you'll need the vertex positions in view space.\nReferrer back to [SSAO](ssao.md#vertex-positions) for details.\n\n### Vertex Normals\n\nTo compute the reflections, you'll need the vertex normals in view space.\nReferrer back to [SSAO](ssao.md#vertex-normals) for details.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/1cE2vBY.gif\" alt=\"SSR using normal maps.\" title=\"SSR using normal maps.\">\n</p>\n\nHere you see SSR using the normal mapped normals instead of the vertex normals.\nNotice how the reflection follows the ripples in the water versus the more mirror\nlike reflection shown earlier.\n\nTo use the normal maps instead,\nyou'll need to transform the normal mapped normals from tangent space to view space\njust like you did in the lighting calculations.\nYou can see this being done in [normal.frag](../demonstration/shaders/fragment/normal.frag).\n\n### Position Transformations\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Qnsvkc0.gif\" alt=\"Position Transformations\" title=\"Position Transformations\">\n</p>\n\nJust like\n[SSAO](ssao.md),\nSSR goes back and forth between the screen and view space.\nYou'll need the camera lens' projection matrix to transform points in view space to clip space.\nFrom clip space, you'll have to transform the points again to UV space.\nOnce in UV space,\nyou can sample a vertex/fragment position from the scene\nwhich will be the closest position in the scene to your sample.\nThis is the _screen space_ part in _screen space reflection_\nsince the \"screen\" is a texture UV mapped over a screen shaped rectangle.\n\n### Reflected UV Coordinates\n\nThere are a few ways you can implement SSR.\nThe example code starts the reflection process by computing a reflected UV coordinate for each screen fragment.\nYou could skip this part and go straight to computing the reflected color instead, using the final rendering of the scene.\n\nRecall that UV coordinates range from zero to one for both U and V.\nThe screen is just a 2D texture UV mapped over a screen-sized rectangle.\nKnowing this, the example code doesn't actually need the final rendering of the scene\nto compute the reflections.\nIt can instead calculate what UV coordinate each screen pixel will eventually use.\nThese calculated UV coordinates can be saved to a framebuffer texture\nand used later when the scene has been rendered.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/pkQcGkD.gif\" alt=\"Reflected UVs\" title=\"Reflected UVs\">\n</p>\n\nHere you see the reflected UV coordinates.\nWithout even rendering the scene yet,\nyou can get a good feel for what the reflections will look like.\n\n```c\n//...\n\nuniform mat4 lensProjection;\n\nuniform sampler2D positionTexture;\nuniform sampler2D normalTexture;\n\n//...\n```\n\nYou'll need the camera lens' projection matrix as well as the interpolated vertex positions and normals in view space.\n\n```c\n  // ...\n\n  float maxDistance = 15;\n  float resolution  = 0.3;\n  int   steps       = 10;\n  float thickness   = 0.5;\n\n  // ...\n```\n\nLike the other effects, SSR has a few parameters you can adjust.\nDepending on the complexity of the scene, it may take you awhile to find the right settings.\nGetting screen space reflections to look just right tends to be difficult when reflecting complex geometry.\n\nThe `maxDistance` parameter controls how far a fragment can reflect.\nIn other words, it controls the maximum length or magnitude of the reflection ray.\n\nThe `resolution` parameter controls how many fragments are skipped while traveling or marching the reflection ray during the first pass.\nThis first pass is to find a point along the ray's direction where the ray enters or goes behind some geometry in the scene.\nThink of this first pass as the rough pass.\nNote that the `resolution` ranges from zero to one.\nZero will result in no reflections while one will travel fragment-by-fragment along the ray's direction.\nA `resolution` of one can slow down your FPS considerably especially with a large `maxDistance`.\n\nThe `steps` parameter controls how many iterations occur during the second pass.\nThis second pass is to find the exact point along the reflection ray's direction\nwhere the ray immediately hits or intersects with some geometry in the scene.\nThink of this second pass as the refinement pass.\n\nThe `thickness` controls the cutoff between what counts as a possible reflection hit and what does not.\nIdeally, you'd like to have the ray immediately stop at some camera-captured position or depth in the scene.\nThis would be the exact point where the light ray bounced off, hit your current fragment, and then bounced off into the camera.\nUnfortunately the calculations are not always that precise so `thickness` provides some wiggle room or tolerance.\nYou'll want the thickness to be as small as possible—just a short distance beyond a sampled position or depth.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/W163Mfw.png\" alt=\"Larger Thickness\" title=\"Larger Thickness\">\n</p>\n\nYou'll find that as the thickness gets larger, the reflections tend to smear in places.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/T6uH4Yu.png\" alt=\"Smaller Thickness\" title=\"Smaller Thickness\">\n</p>\n\nGoing in the other direction, as the thickness gets smaller,\nthe reflections become noisy with tiny little holes and narrow gaps.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(positionTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 positionFrom     = texture(positionTexture, texCoord);\n  vec3 unitPositionFrom = normalize(positionFrom.xyz);\n  vec3 normal           = normalize(texture(normalTexture, texCoord).xyz);\n  vec3 pivot            = normalize(reflect(unitPositionFrom, normal));\n\n  // ...\n```\n\nGather the current fragment's position, normal, and reflection about the normal.\n`positionFrom` is a vector from the camera position to the current fragment position.\n`normal` is a vector pointing in the direction of the interpolated vertex normal for the current fragment.\n`pivot` is the reflection ray or vector pointing in the reflected direction of the `positionFrom` vector.\nIt currently has a length or magnitude of one.\n\n\n```c\n  // ...\n\n  vec4 startView = vec4(positionFrom.xyz + (pivot *           0), 1);\n  vec4 endView   = vec4(positionFrom.xyz + (pivot * maxDistance), 1);\n\n  // ...\n```\n\nCalculate the start and end point of the reflection ray in view space.\n\n```c\n  // ...\n\n  vec4 startFrag      = startView;\n       // Project to screen space.\n       startFrag      = lensProjection * startFrag;\n       // Perform the perspective divide.\n       startFrag.xyz /= startFrag.w;\n       // Convert the screen-space XY coordinates to UV coordinates.\n       startFrag.xy   = startFrag.xy * 0.5 + 0.5;\n       // Convert the UV coordinates to fragment/pixel coordnates.\n       startFrag.xy  *= texSize;\n\n  vec4 endFrag      = endView;\n       endFrag      = lensProjection * endFrag;\n       endFrag.xyz /= endFrag.w;\n       endFrag.xy   = endFrag.xy * 0.5 + 0.5;\n       endFrag.xy  *= texSize;\n\n  // ...\n```\n\nProject or transform these start and end points from view space to screen space.\nThese points are now fragment positions which correspond to pixel positions on the screen.\nNow that you know where the ray starts and ends on the screen, you can travel or march along its direction in screen space.\nThink of the ray as a line drawn on the screen.\nYou'll travel along this line using it to sample the fragment positions stored in the position framebuffer texture.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MpBR225.png\" alt=\"Screen space versus view space.\" title=\"Screen space versus view space.\">\n</p>\n\nNote that you could march the ray through view space\nbut this may under or over sample scene positions found in the position framebuffer texture.\nRecall that the position framebuffer texture is the size and shape of the screen.\nEvery screen fragment or pixel corresponds to some position captured by the camera.\nA reflection ray may travel a long distance in view space, but in screen space, it may only travel through a few pixels.\nYou can only sample the screen's pixels for positions\nso it is inefficient to potentially sample the same pixels over and over again while marching in view space.\nBy marching in screen space, you'll more efficiently sample the fragments or pixels the ray actually occupies or covers.\n\n```c\n  // ...\n\n  vec2 frag  = startFrag.xy;\n       uv.xy = frag / texSize;\n\n  // ...\n```\n\nThe first pass will begin at the starting fragment position of the reflection ray.\nConvert the fragment position to a UV coordinate by dividing the fragment's coordinates by the position texture's dimensions.\n\n```c\n  // ...\n\n  float deltaX    = endFrag.x - startFrag.x;\n  float deltaY    = endFrag.y - startFrag.y;\n\n  // ...\n```\n\nCalculate the delta or difference between the X and Y coordinates of the end and start fragments.\nThis will be how many pixels the ray line occupies in the X and Y dimension of the screen.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Um4dzgL.png\" alt=\"The reflection ray in screen space.\" title=\"The reflection ray in screen space.\">\n</p>\n\n```c\n  // ...\n\n  float useX      = abs(deltaX) >= abs(deltaY) ? 1 : 0;\n  float delta     = mix(abs(deltaY), abs(deltaX), useX) * clamp(resolution, 0, 1);\n\n  // ...\n```\n\nTo handle all of the various different ways (vertical, horizontal, diagonal, etc.) the line can be oriented,\nyou'll need to keep track of and use the larger difference.\nThe larger difference will help you determine\nhow much to travel in the X and Y direction each iteration,\nhow many iterations are needed to travel the entire line,\nand what percentage of the line does the current position represent.\n\n`useX` is either one or zero.\nIt is used to pick the X or Y dimension depending on which delta is bigger.\n`delta` is the larger delta of the two X and Y deltas.\nIt is used to determine how much to march in either dimension each iteration and how many iterations to take during the first pass.\n\n```c\n  // ...\n\n  vec2  increment = vec2(deltaX, deltaY) / max(delta, 0.001);\n\n  // ...\n```\n\nCalculate how much to increment the X and Y position by using the larger of the two deltas.\nIf the two deltas are the same, each will increment by one each iteration.\nIf one delta is larger than the other, the larger delta will increment by one while the smaller one will increment by less than one.\nThis assumes the `resolution` is one.\nIf the resolution is less than one, the algorithm will skip over fragments.\n\n```c\nstartFrag  = ( 1,  4)\nendFrag    = (10, 14)\n\ndeltaX     = (10 - 1) = 9\ndeltaY     = (14 - 4) = 10\n\nresolution = 0.5\n\ndelta      = 10 * 0.5 = 5\n\nincrement  = (deltaX, deltaY) / delta\n           = (     9,     10) / 5\n           = ( 9 / 5,      2)\n```\n\nFor example, say the `resolution` is 0.5.\nThe larger dimension will increment by two fragments instead of one.\n\n\n```c\n  // ...\n\n  float search0 = 0;\n  float search1 = 0;\n\n  // ...\n```\n\nTo move from the start fragment to the end fragment, the algorithm uses linear interpolation.\n\n```c\ncurrent position x = (start x) * (1 - search1) + (end x) * search1;\ncurrent position y = (start y) * (1 - search1) + (end y) * search1;\n```\n\n`search1` ranges from zero to one.\nWhen `search1` is zero, the current position is the start fragment.\nWhen `search1` is one, the current position is the end fragment.\nFor any other value, the current position is somewhere between the start and end fragment.\n\n`search0` is used to remember the last position on the line where the ray missed or didn't intersect with any geometry.\nThe algorithm will later use `search0` in the second pass to help refine the point at which the ray touches the scene's geometry.\n\n```c\n  // ...\n\n  int hit0 = 0;\n  int hit1 = 0;\n\n  // ...\n```\n\n`hit0` indicates there was an intersection during the first pass.\n`hit1` indicates there was an intersection during the second pass.\n\n```c\n  // ...\n\n  float viewDistance = startView.y;\n  float depth        = thickness;\n\n  // ...\n```\n\nThe `viewDistance` value is how far away from the camera the current point on the ray is.\nRecall that for Panda3D, the Y dimension goes in and out of the screen in view space.\nFor other systems, the Z dimension goes in and out of the screen in view space.\nIn any case, `viewDistance` is how far away from the camera the ray currently is.\nNote that if you use the depth buffer, instead of the vertex positions in view space, the `viewDistance` would be the Z depth.\n\nMake sure not to confuse the `viewDistance` value with the Y dimension of the line being traveled across the screen.\nThe `viewDistance` goes from the camera into scene while the Y dimension of the line travels up or down the screen.\n\nThe `depth` is the view distance difference between the current ray point and scene position.\nIt tells you how far behind or in front of the scene the ray currently is.\nRemember that the scene positions are the interpolated vertex positions stored in the position framebuffer texture.\n\n```c\n  // ...\n\n  for (i = 0; i < int(delta); ++i) {\n\n  // ...\n```\n\nYou can now begin the first pass.\nThe first pass runs while `i` is less than the `delta` value.\nWhen `i` reaches `delta`, the algorithm has traveled the entire length of the line.\nRemember that `delta` is the larger of the two X and Y deltas.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Qnsvkc0.gif\" alt=\"Screen Space Transformations\" title=\"Screen Space Transformations\">\n</p>\n\n```c\n    // ...\n\n    frag      += increment;\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionTexture, uv.xy);\n\n    // ...\n```\n\nAdvance the current fragment position closer to the end fragment.\nUse this new fragment position to look up a scene position stored in the position framebuffer texture.\n\n```c\n    // ...\n\n    search1 =\n      mix\n        ( (frag.y - startFrag.y) / deltaY\n        , (frag.x - startFrag.x) / deltaX\n        , useX\n        );\n\n    // ...\n```\n\nCalculate the percentage or portion of the line the current fragment represents.\nIf `useX` is zero, use the Y dimension of the line.\nIf `useX` is one, use the X dimension of the line.\n\nWhen `frag` equals `startFrag`,\n`search1` equals zero since `frag - startFrag` is zero.\nWhen `frag` equals `endFrag`,\n`search1` is one since `frag - startFrag` equals `delta`.\n\n`search1` is the percentage or portion of the line the current position represents.\nYou'll need this to interpolate between the ray's view-space start and end distances from the camera.\n\n```c\n    // ...\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n\n    // ...\n```\n\nUsing `search1`,\ninterpolate the view distance (distance from the camera in view space) for the current position you're at on the reflection ray.\n\n```c\n// Incorrect.\nviewDistance = mix(startView.y, endView.y, search1);\n\n// Correct.\nviewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n```\n\nYou may be tempted to just interpolate between the view distances of the start and end view-space positions\nbut this will give you the wrong view distance for the current position on the reflection ray.\nInstead, you'll need to perform\n[perspective-correct interpolation](https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf)\nwhich you see here.\n\n```c\n    // ...\n\n    depth        = viewDistance - positionTo.y;\n\n    // ...\n```\n\nCalculate the difference between the ray's view distance at this point and the sampled view distance of the scene at this point.\n\n```c\n    // ...\n\n    if (depth > 0 && depth < thickness) {\n      hit0 = 1;\n      break;\n    } else {\n      search0 = search1;\n    }\n\n    // ...\n```\n\nIf the difference is between zero and the thickness,\nthis is a hit.\nSet `hit0` to one and exit the first pass.\nIf the difference is not between zero and the thickness,\nthis is a miss.\nSet `search0` to equal `search1` to remember this position as the last known miss.\nContinue marching the ray towards the end fragment.\n\n```c\n  // ...\n\n  search1 = search0 + ((search1 - search0) / 2);\n\n  // ...\n```\n\nAt this point you have finished the first pass.\nSet the `search1` position to be halfway between the position of the last miss and the position of the last hit.\n\n```c\n  // ...\n\n  steps *= hit0;\n\n  for (i = 0; i < steps; ++i) {\n\n  // ...\n```\n\nYou can now begin the second pass.\nIf the reflection ray didn't hit anything in the first pass, skip the second pass.\n\n```c\n    // ...\n\n    frag       = mix(startFrag.xy, endFrag.xy, search1);\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionTexture, uv.xy);\n\n    // ...\n```\n\nAs you did in the first pass,\nuse the current position on the ray line to sample a position from the scene.\n\n```c\n    // ...\n\n    viewDistance = (startView.y * endView.y) / mix(endView.y, startView.y, search1);\n    depth        = viewDistance - positionTo.y;\n\n    // ...\n```\n\nInterpolate the view distance for the current ray line position\nand calculate the camera distance difference between the ray at this point and the scene.\n\n```c\n    // ...\n\n    if (depth > 0 && depth < thickness) {\n      hit1 = 1;\n      search1 = search0 + ((search1 - search0) / 2);\n    } else {\n      float temp = search1;\n      search1 = search1 + ((search1 - search0) / 2);\n      search0 = temp;\n    }\n\n    // ...\n```\n\nIf the depth is within bounds, this is a hit.\nSet `hit1` to one and set `search1` to be halfway between the last known miss position and this current hit position.\nIf the depth is not within bounds, this is a miss.\nSet `search1` to be halfway between this current miss position and the last known hit position.\nMove `search0` to this current miss position.\nContinue this back and forth search while `i` is less than `steps`.\n\n```c\n  // ...\n\n  float visibility =\n      hit1\n\n  // ...\n```\n\nYou're now done with the second and final pass but before you can output the reflected UV coordinates,\nyou'll need to calculate the `visibility` of the reflection.\nThe `visibility` ranges from zero to one.\nIf there wasn't a hit in the second pass, the `visibility` is zero.\n\n```c\n  // ...\n\n    * positionTo.w\n\n  // ...\n```\n\nIf the reflected scene position's alpha or `w` component is zero,\nthe `visibility` is zero.\nNote that if `w` is zero, there was no scene position at that point.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/7e2cOdZ.gif\" alt=\"Reflection ray pointing towards the camera position.\" title=\"Reflection ray pointing towards the camera position.\">\n</p>\n\n```c\n  // ...\n\n    * ( 1\n      - max\n         ( dot(-unitPositionFrom, pivot)\n         , 0\n         )\n      )\n\n  // ...\n```\n\nOne of the ways in which screen space reflection can fail is when the reflection ray points in the general direction of the camera.\nIf the reflection ray points towards the camera and hits something, it's most likely hitting the back side of something facing away\nfrom the camera.\n\nTo handle this failure case, you'll need to gradually fade out the reflection based\non how much the reflection vector points to the camera's position.\nIf the reflection vector points directly in the opposite direction of the position vector,\nthe visibility is zero.\nAny other direction results in the visibility being greater than zero.\n\nRemember to normalize both vectors when taking the dot product.\n`unitPositionFrom` is the normalized position vector.\nIt has a length or magnitude of one.\n\n```c\n  // ...\n\n    * ( 1\n      - clamp\n          ( depth / thickness\n          , 0\n          , 1\n          )\n      )\n\n  // ...\n```\n\nAs you sample scene positions along the reflection ray,\nyou're hoping to find the exact point where the reflection ray first intersects with the scene's geometry.\nUnfortunately, you may not find this particular point.\nFade out the reflection the further it is from the intersection point you did find.\n\n```c\n  // ...\n\n    * ( 1\n      - clamp\n          (   length(positionTo - positionFrom)\n            / maxDistance\n          , 0\n          , 1\n          )\n      )\n\n  // ...\n```\n\nFade out the reflection based on how far way the reflected point is from the initial starting point.\nThis will fade out the reflection instead of it ending abruptly as it reaches `maxDistance`.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/i0btBna.gif\" alt=\"Reflection ray exiting the frustum.\" title=\"Reflection ray exiting the frustum.\">\n</p>\n\n\n```c\n  // ...\n\n    * (uv.x < 0 || uv.x > 1 ? 0 : 1)\n    * (uv.y < 0 || uv.y > 1 ? 0 : 1);\n\n  // ...\n```\n\nIf the reflected UV coordinates are out of bounds, set the `visibility` to zero.\nThis occurs when the reflection ray travels outside the camera's frustum.\n\n```c\n  visibility = clamp(visibility, 0, 1);\n\n  uv.ba = vec2(visibility);\n```\n\nSet the blue and alpha component to the visibility as the UV coordinates only need the RG or XY components of the final vector.\n\n```c\n  // ...\n\n  fragColor = uv;\n\n  // ...\n```\n\nThe final fragment color is the reflected UV coordinates and the visibility.\n\n### Specular Map\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuFYVWB.gif\" alt=\"Specular Map\" title=\"Specular Map\">\n</p>\n\nIn addition to the reflected UV coordinates, you'll also need a specular map.\nThe example code creates one using the fragment's material specular properties.\n\n```c\n// ...\n\n#define MAX_SHININESS 127.75\n\nuniform struct\n  { vec3 specular\n  ; float shininess\n  ;\n  } p3d_Material;\n\nout vec4 fragColor;\n\nvoid main() {\n  fragColor =\n    vec4\n      ( p3d_Material.specular\n      , clamp(p3d_Material.shininess / MAX_SHININESS, 0, 1)\n      );\n}\n```\n\nThe specular fragment shader is quite simple.\nUsing the fragment's material,\nthe shader outputs the specular color and uses the alpha channel for the shininess.\nThe shininess is mapped to a range of zero to one.\nIn Blender, the maximum specular hardness or shininess is 511.\nWhen exporting from Blender to Panda3D, 511 is exported as 127.75.\nFeel free to adjust the shininess to range of zero to one however you see fit for your particular stack.\n\nThe example code generates a specular map from the material specular properties but you\ncould create one in GIMP, for example, and attach that as a texture to your 3D model.\nFor instance,\nsay your 3D treasure chest has shiny brackets on it but nothing else should reflect the environment.\nYou can paint the brackets some shade of gray and the rest of the treasure chest black.\nThis will mask off the brackets, allowing your shader to render the reflections on only the brackets\nand nothing else.\n\n### Scene Colors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/diBSxPI.png\" alt=\"Scene Colors\" title=\"Scene Colors\">\n</p>\n\nYou'll need to render the parts of the scene you wish to reflect and store this in a framebuffer texture.\nThis is typically just the scene without any reflections.\n\n### Reflected Scene Colors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/UPvgfDU.gif\" alt=\"Reflected Scene Colors\" title=\"Reflected Scene Colors\">\n</p>\n\nHere you see the reflected colors saved to a framebuffer texture.\n\n```c\n// ...\n\nuniform sampler2D uvTexture;\nuniform sampler2D colorTexture;\n\n// ...\n```\n\nOnce you have the reflected UV coordinates, looking up the reflected colors is fairly easy.\nYou'll need the reflected UV coordinates texture and the color texture containing the colors you wish to reflect.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(uvTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv    = texture(uvTexture,    texCoord);\n  vec4 color = texture(colorTexture, uv.xy);\n\n  // ...\n```\n\nUsing the UV coordinates for the current fragment, look up the reflected color.\n\n```c\n  // ...\n\n  float alpha = clamp(uv.b, 0, 1);\n\n  // ...\n```\n\nRecall that the reflected UV texture stored the visibility in the B or blue component.\nThis is the alpha channel for the reflected colors framebuffer texture.\n\n```c\n  // ...\n\n  fragColor = vec4(mix(vec3(0), color.rgb, alpha), alpha);\n\n  // ...\n```\n\nThe fragment color is a mix between no reflection and the reflected color based on the visibility.\nThe visibility was computed during the reflected UV coordinates step.\n\n### Blurred Reflected Scene Colors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/gVvx1Ei.png\" alt=\"Blurred Reflected Scene Colors\" title=\"Blurred Reflected Scene Colors\">\n</p>\n\nNow blur the reflected scene colors and store this in a framebuffer texture.\nThe blurring is done using a box blur.\nRefer to the [SSAO blurring](ssao.md#blurring) step for details.\n\nThe blurred reflected colors are used for surfaces that have a less than mirror like finish.\nThese surfaces have tiny little hills and valleys that tend to diffuse or blur the reflection.\nI'll cover this more during the roughness calculation.\n\n### Reflections\n\n```c\n// ...\n\nuniform sampler2D colorTexture;\nuniform sampler2D colorBlurTexture;\nuniform sampler2D specularTexture;\n\n// ...\n```\n\nTo generate the final reflections, you'll need the three framebuffer textures computed earlier.\nYou'll need the reflected colors, the blurred reflected colors, and the specular map.\n\n```c\n  // ...\n\n  vec4 specular  = texture(specularTexture,  texCoord);\n  vec4 color     = texture(colorTexture,     texCoord);\n  vec4 colorBlur = texture(colorBlurTexture, texCoord);\n\n  // ...\n```\n\nLook up the specular amount and shininess, the reflected scene color, and the blurred reflected scene color.\n\n```c\n  // ...\n\n  float specularAmount = dot(specular.rgb, vec3(1)) / 3;\n\n  if (specularAmount <= 0) { fragColor = vec4(0); return; }\n\n  // ...\n```\n\nMap the specular color to a greyscale value.\nIf the specular amount is none, set the frag color to nothing and return.\n\nLater on, you'll multiply the final reflection color by the specular amount.\nMultiplying by the specular amount allows you to control how much a material reflects its environment\nsimply by brightening or darkening the greyscale value in the specular map.\n\n```c\n  dot(specular.rgb, vec3(1)) == (specular.r + specular.g + specular.b);\n```\n\nUsing the dot product to produce the greyscale value is just a short way of summing the three color components.\n\n```c\n  // ...\n\n  float roughness = 1 - min(specular.a, 1);\n\n  // ...\n```\n\nCalculate the roughness based on the shininess value set during the specular map step.\nRecall that the shininess value was saved in the alpha channel of the specular map.\nThe shininess determined how spread out or blurred the specular reflection was.\nSimilarly, the `roughness` determines how blurred the reflection is.\nA roughness of one will produce the blurred reflection color.\nA roughness of zero will produce the non-blurred reflection color.\nDoing it this way allows you to control how blurred the reflection is just by changing\nthe material's shininess value.\n\nThe example code generates a roughness map from the material specular properties but you\ncould create one in GIMP, for example, and attach that as a texture to your 3D model.\nFor instance, say you have a tiled floor that has polished tiles and scratched up tiles.\nThe polished tiles could be painted a more translucent white while the scratched up tiles could be painted a more opaque white.\nThe more translucent/transparent the greyscale value, the more the shader will use the blurred reflected color.\nThe scratched tiles will have a blurry reflection while the polished tiles will have a mirror like reflection.\n\n```c\n  // ...\n\n  fragColor = mix(color, colorBlur, roughness) * specularAmount;\n\n  // ...\n```\n\nMix the reflected color and blurred reflected color based on the roughness.\nMultiply that vector by the specular amount and then set that value as the fragment color.\n\nThe reflection color is a mix between the reflected scene color and the blurred reflected scene color based on the roughness.\nA high roughness will produce a blurry reflection meaning the surface is rough.\nA low roughness will produce a clear reflection meaning the surface is smooth.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [normal.frag](../demonstration/shaders/fragment/normal.frag)\n- [material-specular.frag](../demonstration/shaders/fragment/material-specular.frag)\n- [screen-space-reflection.frag](../demonstration/shaders/fragment/screen-space-reflection.frag)\n- [reflection-color.frag](../demonstration/shaders/fragment/reflection-color.frag)\n- [reflection.frag](../demonstration/shaders/fragment/reflection.frag)\n- [box-blur.frag](../demonstration/shaders/fragment/box-blur.frag)\n- [base-combine.frag](../demonstration/shaders/fragment/base-combine.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](chromatic-aberration.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](screen-space-refraction.md)\n"
  },
  {
    "path": "sections/screen-space-refraction.md",
    "content": "[:arrow_backward:](screen-space-reflection.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](foam.md)\n\n# 3D Game Shaders For Beginners\n\n## Screen Space Refraction (SSR)\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/8Mdcn4y.gif\" alt=\"Screen Space Refraction\" title=\"Screen Space Refraction\">\n</p>\n\nScreen space refraction,\nmuch like\n[screen space reflection](screen-space-reflection.md),\nadds a touch of realism you can't find anywhere else.\nGlass, plastic, water, and other transparent/translucent materials spring to life.\n\n[Screen space reflection](screen-space-reflection.md)\nand screen space refraction work almost identically expect for one major difference.\nInstead of using the reflected vector, screen space refraction uses the\n[refracted vector](http://asawicki.info/news_1301_reflect_and_refract_functions.html).\nIt's a slight change in code but a big difference visually.\n\n### Vertex Positions\n\nLike SSAO, you'll need the vertex positions in view space.\nReferrer back to [SSAO](ssao.md#vertex-positions) for details.\n\nHowever,\nunlike SSAO,\nyou'll need the scene's vertex positions with and without the refractive objects.\nRefractive surfaces are translucent, meaning you can see through them.\nSince you can see through them, you'll need the vertex positions behind the refractive surface.\nHaving both the foreground and background vertex positions will allow you to calculate\nUV coordinates and depth.\n\n### Vertex Normals\n\nTo compute the refractions, you'll need the scene's foreground vertex normals in view space.\nThe background vertex normals aren't needed unless you need to incorporate the background surface detail\nwhile calculating the refracted UV coordinates and distances.\nReferrer back to [SSAO](ssao.md#vertex-normals) for details.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/MZ2R8I6.gif\" alt=\"Normal maps versus no normal maps.\" title=\"Normal maps versus no normal maps.\">\n</p>\n\nHere you see the water refracting the light with and without normal maps.\nIf available, be sure to use the normal mapped normals instead of the vertex normals.\nThe smoother and flatter the surface, the harder it is to tell the light is being refracted.\nThere will be some distortion but not enough to make it worthwhile.\n\nTo use the normal maps instead,\nyou'll need to transform the normal mapped normals from tangent space to view space\njust like you did in the [lighting](normal-mapping.md#fragment) calculations.\nYou can see this being done in [normal.frag](../demonstration/shaders/fragment/normal.frag).\n\n### Position Transformations\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/bXtXDyu.gif\" alt=\"Position Transformations\" title=\"Position Transformations\">\n</p>\n\nJust like\n[SSAO](ssao.md) and [screen space reflection](screen-space-reflection.md),\nscreen space refraction goes back and forth between the screen and view space.\nYou'll need the camera lens' projection matrix to transform points in view space to clip space.\nFrom clip space, you'll have to transform the points again to UV space.\nOnce in UV space,\nyou can sample a vertex/fragment position from the scene\nwhich will be the closest position in the scene to your sample.\nThis is the _screen space_ part in _screen space refraction_\nsince the \"screen\" is a texture UV mapped over a screen shaped rectangle.\n\n### Refracted UV Coordinates\n\nRecall that UV coordinates range from zero to one for both U and V.\nThe screen is just a 2D texture UV mapped over a screen-sized rectangle.\nKnowing this, the example code doesn't actually need the final rendering of the scene\nto compute the refraction.\nIt can instead calculate what UV coordinate each screen pixel will eventually use.\nThese calculated UV coordinates can be saved to a framebuffer texture\nand used later when the scene has been rendered.\n\nThe process of refracting the UV coordinates is very similar to the process of\n[reflecting the UV coordinates](screen-space-reflection.md#reflected-uv-coordinates).\nBelow are the adjustments you'll need to turn reflection into refraction.\n\n```c\n// ...\n\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\nuniform sampler2D normalFromTexture;\n\n// ...\n```\n\nReflection only deals with what is in front of the reflective surface.\nRefraction, however, deals with what is behind the refractive surface.\nTo accommodate this, you'll need both the vertex positions of the scene\nwith the refracting surfaces taken out and the vertex positions of the scene\nwith the refracting surfaces left in.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/FjQtjsm.gif\" alt=\"Without and with refractive surfaces.\" title=\"Without and with refractive surfaces.\">\n</p>\n\n`positionFromTexture` are the scene's vertex positions with the refracting surfaces left in.\n`positionToTexture` are the scene's vertex positions with the refracting surfaces taken out.\n`normalFromTexture` are the scene's vertex normals with the refraction surfaces left in.\nThere's no need for the vertex normals behind the refractive surfaces unless\nyou want to incorporate the surface detail for the background geometry.\n\n```c\n// ...\n\nuniform vec2 rior;\n\n// ...\n```\n\nRefraction has one more adjustable parameter than reflection.\n`rior` is the relative index of refraction or relative refractive index.\nIt is the ratio of the refraction indexes of two mediums.\nSo for example, going from water to air is `1 / 1.33 ≈ 0.75`.\nThe numerator is the refractive index of the medium the light is leaving and\nthe denominator is the refractive index of the medium the light is entering.\nAn `rior` of one means the light passes right through without being refracted or bent.\nAs `rior` grows, the refraction will become a\n[reflection](https://en.wikipedia.org/wiki/Total_internal_reflection).\n\nThere's no requirement that `rior` must adhere to the real world.\nThe demo uses `1.05`.\nThis is completely unrealistic (light does not travel faster through water than air)\nbut the realistic setting produced too many artifacts.\nIn the end, the distortion only has to be believable—not realistic.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/dDOnobK.gif\" alt=\"Adjusting the relative index of refraction.\" title=\"Adjusting the relative index of refraction.\">\n</p>\n\n`rior` values above one tend to elongate the refraction while\nnumbers below one tend to shrink the refraction.\n\nAs it was with screen space reflection,\nthe screen doesn't have the entire geometry of the scene.\nA refracted ray may march through the screen space and never hit a captured surface.\nOr it may hit a surface but it's the backside not captured by the camera.\nWhen this happened during reflection, the fragment was left blank.\nThis indicated no reflection or not enough information to determine a reflection.\nLeaving the fragment blank was fine for reflection since the reflective surface would fill in the gaps.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/vcQDAYU.gif\" alt=\"Refraction Holes\" title=\"Refraction Holes\">\n</p>\n\nFor refraction, however, we must set the fragment to some UV.\nIf the fragment is left blank,\nthe refractive surface will contain holes that let the detail behind it come through.\nThis would be okay for a completely transparent surface but usually\nthe refractive surface will have some tint to it, reflection, etc.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(positionFromTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv = vec4(texCoord.xy, 1, 1);\n\n  // ...\n```\n\nThe best choice is to select the UV as if the `rior` was one.\nThis will leave the UV coordinate unchanged,\nallowing the background to show through instead\nof there being a hole in the refractive surface.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/9fybLUO.png\" alt=\"Refraction UV Map\" title=\"Refraction UV Map\">\n</p>\n\nHere you see the refracted UV texture for the mill scene.\nThe wheel and waterway disturb what is otherwise a smooth gradient.\nThe disruptions shift the UV coordinates from their screen position to their refracted screen position.\n\n```c\n  // ...\n\n  vec3 unitPositionFrom = normalize(positionFrom.xyz);\n  vec3 normalFrom       = normalize(texture(normalFromTexture, texCoord).xyz);\n  vec3 pivot            = normalize(refract(unitPositionFrom, normalFrom, rior.x));\n\n  // ...\n```\n\nThe most important difference is the calculation of the refracted vector versus the reflected vector.\nBoth use the unit position and normal but `refract` takes an additional parameter specifying the relative refractive index.\n\n```c\n    // ...\n\n    frag      += increment;\n    uv.xy      = frag / texSize;\n    positionTo = texture(positionToTexture, uv.xy);\n\n    // ...\n```\n\nThe `positionTo`, sampled by the `uv` coordinates, uses the `positionToTexture`.\nFor reflection,\nyou only need one framebuffer texture containing the scene's interpolated vertex positions in view space.\nHowever,\nfor refraction,\n`positionToTexture` contains the vertex positions of the scene minus the refractive surfaces since\nthe refraction ray typically goes behind the surface.\nIf `positionFromTexture` and `positionToTexture` were the same for refraction,\nthe refracted ray would hit the refractive surface instead of what is behind it.\n\n### Refraction Mask\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/iuFYVWB.gif\" alt=\"Material Specular\" title=\"Material Specular\">\n</p>\n\nYou'll need a mask to filter out the non-refractive parts of the scene.\nThis mask will determine which fragment does and does not receive a refracted color.\nYou could use this mask during the refracted UV calculation step or later when you\nactually sample the colors at the refracted UV coordinates.\n\nThe mill scene uses the models' material specular as a mask.\nFor the demo's purposes,\nthe specular map is sufficient but you may want to use a more specialized map.\nRefer back to [screen space reflection](screen-space-reflection.md#specular-map) for how to render the specular map.\n\n## Background Colors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/AmT9RrU.gif\" alt=\"Background Colors\" title=\"Background Colors\">\n</p>\n\nYou'll need to render the parts of the scene behind the refractive objects.\nThis can be done by hiding the refractive objects and then rendering the scene to a framebuffer texture.\n\n### Foreground Colors\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/6RPHULr.gif\" alt=\"Foreground Colors\" title=\"Foreground Colors\">\n</p>\n\n```c\n// ...\n\nuniform sampler2D uvTexture;\nuniform sampler2D refractionMaskTexture;\nuniform sampler2D positionFromTexture;\nuniform sampler2D positionToTexture;\nuniform sampler2D backgroundColorTexture;\n\n// ...\n```\n\nTo render the actual refractions or foreground colors,\nyou'll need the refracted UV coordinates,\nrefraction mask,\nthe foreground and background vertex positions,\nand the background colors.\n\n```c\n  // ...\n\n  vec3  tintColor = vec3(0.27, 0.58, 0.92, 0.3);\n  float depthMax  = 2;\n\n  // ...\n```\n\n`tintColor` and `depthMax` are adjustable parameters.\n`tintColor` colorizes the background color.\n`depthMax` ranges from zero to infinity.\nWhen the distance between the foreground and background position reaches `depthMax`,\nthe foreground color will be the fully tinted background color.\nAt distance zero, the foreground will be the background color.\n\n```c\n  // ...\n\n  vec2 texSize  = textureSize(backgroundColorTexture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  vec4 uv              = texture(uvTexture,              texCoord);\n  vec4 mask            = texture(maskTexture,            texCoord);\n  vec4 positionFrom    = texture(positionFromTexture,    texCoord);\n  vec4 positionTo      = texture(positionToTexture,      uv.xy);\n  vec4 backgroundColor = texture(backgroundColorTexture, uv.xy);\n\n  if (refractionMask.r <= 0) { fragColor = vec4(0); return; }\n\n  // ...\n```\n\nPull out the uv coordinates,\nmask,\nbackground position,\nforeground position,\nand the background color.\n\nIf the refraction mask is turned off for this fragment, return nothing.\n\n```c\n  // ...\n\n  float depth   = length(positionTo.xyz - positionFrom.xyz);\n  float mixture = clamp(depth / depthMax, 0, 1);\n\n  vec3 shallowColor    = backgroundColor.rgb;\n  vec3 deepColor       = mix(shallowColor, tintColor.rgb, tintColor.a);\n  vec3 foregroundColor = mix(shallowColor, deepColor,     mixture);\n\n  // ...\n```\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/IEFKerB.gif\" alt=\"Refraction Depth\" title=\"Refraction Depth\">\n</p>\n\nCalculate the depth or distance between the foreground position and the background position.\nAt zero depth, the foreground color will be the shallow color.\nAt `depthMax`, the foreground color will be the deep color.\nThe deep color is the background color tinted with `tintColor`.\n\n```c\n  // ...\n\n  fragColor = mix(vec4(0), vec4(foregroundColor, 1), uv.b);\n\n  // ...\n```\n\nRecall that the blue channel, in the refracted UV texture, is set to the visibility.\nThe visibility declines as the refracted ray points back at the camera.\nWhile the visibility should always be one, it is put here for completeness.\nAs the visibility lessens, the fragment color will receive less and less of the foreground color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [normal.frag](../demonstration/shaders/fragment/normal.frag)\n- [material-specular.frag](../demonstration/shaders/fragment/material-specular.frag)\n- [screen-space-refraction.frag](../demonstration/shaders/fragment/screen-space-refraction.frag)\n- [refraction.frag](../demonstration/shaders/fragment/refraction.frag)\n- [base-combine.frag](../demonstration/shaders/fragment/base-combine.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](screen-space-reflection.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](foam.md)\n"
  },
  {
    "path": "sections/setup.md",
    "content": "[:arrow_backward:](gamma-correction.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](building-the-demo.md)\n\n# 3D Game Shaders For Beginners\n\n## Setup\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fYpIWNk.gif\" alt=\"Setup\" title=\"Setup\">\n</p>\n\nBelow is the setup used to develop and test the example code.\n\n### Environment\n\nThe example code was developed and tested using the following environment.\n\n- Linux manjaro 5.10.42-1-MANJARO\n- OpenGL renderer string: GeForce GTX 970/PCIe/SSE2\n- OpenGL version string: 4.6.0 NVIDIA 465.31\n- g++ (GCC) 11.1.0\n- Panda3D 1.10.9\n\n### Materials\n\nEach [Blender](https://blender.org) material used to build `mill-scene.egg` has five textures\nin the following order.\n\n- Diffuse\n- Normal\n- Specular\n- Reflection\n- Refraction\n\nBy having the same maps in the same positions for all models,\nthe shaders can be generalized, reducing the need to duplicate code.\n\nIf an object uses its vertex normals, a \"flat blue\" normal map is used.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/tFmKgoH.png\" alt=\"A flat normal map.\" title=\"A flat normal map.\">\n</p>\n\nHere is an example of a flat normal map.\nThe only color it contains is flat blue `(red = 128, green = 128, blue = 255)`.\nThis color represents a unit (length one) normal pointing in the positive z-axis `(0, 0, 1)`.\n\n```c\n(0, 0, 1) =\n  ( round((0 * 0.5 + 0.5) * 255)\n  , round((0 * 0.5 + 0.5) * 255)\n  , round((1 * 0.5 + 0.5) * 255)\n  ) =\n    (128, 128, 255) =\n      ( round(128 / 255 * 2 - 1)\n      , round(128 / 255 * 2 - 1)\n      , round(255 / 255 * 2 - 1)\n      ) =\n        (0, 0, 1)\n```\n\nHere you see the unit normal `(0, 0, 1)`\nconverted to flat blue `(128, 128, 255)`\nand flat blue converted to the unit normal.\nYou'll learn more about this in the [normal mapping](normal-mapping.md) technique.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/R9FgZKx.png\" alt=\"Specular Map\" title=\"Specular Map\">\n</p>\n\nUp above is one of the specular maps used.\nThe red and blue channel work to control the amount of specular reflection seen based on the camera angle.\nThe green channel controls the shininess factor.\nYou'll learn more about this in the [lighting](lighting.md) and [fresnel factor](fresnel-factor.md) sections.\n\nThe reflection and refraction textures mask off the objects that are either reflective, refractive, or both.\nFor the reflection texture, the red channel controls the amount of reflection and the green channel controls how\nclear or blurry the reflection is.\n\n### Panda3D\n\nThe example code uses\n[Panda3D](https://www.panda3d.org/)\nas the glue between the shaders.\nThis has no real influence over the techniques described,\nmeaning you'll be able to take what you learn here and apply it to your stack or game engine of choice.\nPanda3D does provide some conveniences.\nI have pointed these out so you can either find an equivalent convenience provided by your stack or\nreplicate it yourself, if your stack doesn't provide something equivalent.\n\nThree Panda3D configurations were changed for the purposes of the demo program.\nYou can find these in [config.prc](../demonstration/config.prc).\nThe configurations changed were\n`gl-coordinate-system default`,\n`textures-power-2 down`, and\n`textures-auto-power-2 1`.\nRefer to the\n[Panda3D configuration](http://www.panda3d.org/manual/?title=Configuring_Panda3D)\npage in the manual for more details.\n\nPanda3D defaults to a z-up, right-handed coordinate system while OpenGL uses a y-up, right-handed system.\n`gl-coordinate-system default` keeps you from having to translate between the two inside your shaders.\n`textures-auto-power-2 1` allows us to use texture sizes that are not a power of two if the system supports it.\nThis comes in handy when doing SSAO and other screen/window sized related techniques since the screen/window size\nis usually not a power of two.\n`textures-power-2 down` downsizes our textures to a power of two if the system only supports texture sizes being a power of two.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](gamma-correction.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](building-the-demo.md)\n"
  },
  {
    "path": "sections/sharpen.md",
    "content": "[:arrow_backward:](pixelization.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](dilation.md)\n\n# 3D Game Shaders For Beginners\n\n## Sharpen\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/VFDKNvl.gif\" alt=\"Sharpen\" title=\"Sharpen\">\n</p>\n\nThe sharpen effect increases the contrast at the edges of the image.\nThis comes in handy when your graphics are bit too soft.\n\n```c\n  // ...\n\n  float amount = 0.8;\n\n  // ...\n```\n\nYou can control how sharp the result is by adjusting the amount.\nAn amount of zero leaves the image untouched.\nTry negative values for an odd look.\n\n```c\n  // ...\n\n  float neighbor = amount * -1;\n  float center   = amount * 4 + 1;\n\n  // ...\n```\n\nNeighboring fragments are multiplied by `amount * -1`.\nThe current fragment is multiplied by `amount * 4 + 1`.\n\n```c\n  // ...\n\n  vec3 color =\n        texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 1) / texSize).rgb\n      * neighbor\n\n      + texture(sharpenTexture, vec2(gl_FragCoord.x - 1, gl_FragCoord.y + 0) / texSize).rgb\n      * neighbor\n      + texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 0) / texSize).rgb\n      * center\n      + texture(sharpenTexture, vec2(gl_FragCoord.x + 1, gl_FragCoord.y + 0) / texSize).rgb\n      * neighbor\n\n      + texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y - 1) / texSize).rgb\n      * neighbor\n      ;\n\n  // ...\n```\n\nThe neighboring fragments are up, down, left, and right.\nAfter multiplying both the neighbors and the current fragment by their particular values, sum the result.\n\n```c\n    // ...\n\n    fragColor = vec4(color, texture(sharpenTexture, texCoord).a);\n\n    // ...\n```\n\nThis sum is the final fragment color.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [sharpen.frag](../demonstration/shaders/fragment/sharpen.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](pixelization.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](dilation.md)\n"
  },
  {
    "path": "sections/ssao.md",
    "content": "[:arrow_backward:](bloom.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](motion-blur.md)\n\n# 3D Game Shaders For Beginners\n\n## Screen Space Ambient Occlusion (SSAO)\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/o7lCukD.gif\" alt=\"SSAO\" title=\"SSAO\">\n</p>\n\nSSAO is one of those effects you never knew you needed and can't live without once you have it.\nIt can take a scene from mediocre to wow!\nFor fairly static scenes, you can bake ambient occlusion into a texture but for more dynamic scenes, you'll need a shader.\nSSAO is one of the more fairly involved shading techniques, but once you pull it off, you'll feel like a shader master.\n\nBy using only a handful of textures, SSAO can approximate the\n[ambient occlusion](https://en.wikipedia.org/wiki/Ambient_occlusion)\nof a scene.\nThis is faster than trying to compute the ambient occlusion by going through all of the scene's geometry.\nThese handful of textures all originate in screen space giving screen space ambient occlusion its name.\n\n### Inputs\n\nThe SSAO shader will need the following inputs.\n\n- Vertex position vectors in view space.\n- Vertex normal vectors in view space.\n- Sample vectors in tangent space.\n- Noise vectors in tangent space.\n- The camera lens' projection matrix.\n\n### Vertex Positions\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/gr7IxKv.png\" alt=\"Panda3D Vertex Positions\" title=\"Panda3D Vertex Positions\">\n</p>\n\nStoring the vertex positions into a framebuffer texture is not a necessity.\nYou can recreate them from the [camera's depth buffer](http://theorangeduck.com/page/pure-depth-ssao).\nThis being a beginners guide, I'll avoid this optimization and keep it straight forward.\nFeel free to use the depth buffer, however, for your implementation.\n\n```cpp\nPT(Texture) depthTexture =\n  new Texture(\"depthTexture\");\ndepthTexture->set_format\n  ( Texture::Format::F_depth_component32\n  );\n\nPT(GraphicsOutput) depthBuffer =\n  graphicsOutput->make_texture_buffer\n    ( \"depthBuffer\"\n    , 0\n    , 0\n    , depthTexture\n    );\ndepthBuffer->set_clear_color\n  ( LVecBase4f(0, 0, 0, 0)\n  );\n\nNodePath depthCameraNP =\n  window->make_camera();\nDCAST(Camera, depthCameraNP.node())->set_lens\n  ( window->get_camera(0)->get_lens()\n  );\nPT(DisplayRegion) depthBufferRegion =\n  depthBuffer->make_display_region\n    ( 0\n    , 1\n    , 0\n    , 1\n    );\ndepthBufferRegion->set_camera(depthCameraNP);\n```\n\nIf you do decide to use the depth buffer, here's how you can set it up using Panda3D.\n\n```c\nin vec4 vertexPosition;\n\nout vec4 fragColor;\n\nvoid main() {\n  fragColor = vertexPosition;\n}\n```\n\nHere's the simple shader used to render out the view space vertex positions into a framebuffer texture.\nThe more involved work is setting up the framebuffer texture such that the fragment vector components it receives\nare not clamped to `[0, 1]` and that each one has a high enough precision (a high enough number of bits).\nFor example, if a particular interpolated vertex position is `<-139.444444566, 0.00000034343, 2.5>`,\nyou don't want it stored into the texture as `<0.0, 0.0, 1.0>`.\n\n```c\n  // ...\n\n  FrameBufferProperties fbp = FrameBufferProperties::get_default();\n\n  // ...\n\n  fbp.set_rgba_bits(32, 32, 32, 32);\n  fbp.set_rgb_color(true);\n  fbp.set_float_color(true);\n\n  // ...\n```\n\nHere's how the example code sets up the framebuffer texture to store the vertex positions.\nIt wants 32 bits per red, green, blue, and alpha components and disables clamping the values to `[0, 1]`\nThe `set_rgba_bits(32, 32, 32, 32)` call sets the bits and also disables the clamping.\n\n```c\n  glTexImage2D\n    ( GL_TEXTURE_2D\n    , 0\n    , GL_RGB32F\n    , 1200\n    , 900\n    , 0\n    , GL_RGB\n    , GL_FLOAT\n    , nullptr\n    );\n```\n\nHere's the equivalent OpenGL call.\n`GL_RGB32F` sets the bits and also disables the clamping.\n\n<blockquote>\nIf the color buffer is fixed-point, the components of the source and destination\nvalues and blend factors are each clamped to [0, 1] or [−1, 1] respectively for\nan unsigned normalized or signed normalized color buffer prior to evaluating the blend\nequation.\nIf the color buffer is floating-point, no clamping occurs.\n<br>\n<br>\n<footer>\n<a href=\"https://www.khronos.org/registry/OpenGL/specs/gl/glspec44.core.pdf\">Source</a>\n</footer>\n</blockquote>\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/V4nETME.png\" alt=\"OpenGL Vertex Positions\" title=\"OpenGL Vertex Positions\">\n</p>\n\nHere you see the vertex positions with y being the up vector.\n\nRecall that Panda3D sets z as the up vector but OpenGL uses y as the up vector.\nThe position shader outputs the vertex positions with z being up since Panda3D\nwas configured with `gl-coordinate-system default`.\n\n### Vertex Normals\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ilnbkzq.gif\" alt=\"Panda3d Vertex Normals\" title=\"Panda3d Vertex Normals\">\n</p>\n\nYou'll need the vertex normals to correctly orient the samples you'll take in the SSAO shader.\nThe example code generates multiple sample vectors distributed in a hemisphere\nbut you could use a sphere and do away with the need for normals all together.\n\n```c\nin vec3 vertexNormal;\n\nout vec4 fragColor;\n\nvoid main() {\n  vec3 normal = normalize(vertexNormal);\n\n  fragColor = vec4(normal, 1);\n}\n```\n\nLike the position shader, the normal shader is simple as well.\nBe sure to normalize the vertex normal and remember that they are in view space.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/ucdx9Kp.gif\" alt=\"OpenGL Vertex Normals\" title=\"OpenGL Vertex Normals\">\n</p>\n\nHere you see the vertex normals with y being the up vector.\n\nRecall that Panda3D sets z as the up vector but OpenGL uses y as the up vector.\nThe normal shader outputs the vertex positions with z being up since Panda3D\nwas configured with `gl-coordinate-system default`.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/fiHXBex.gif\" alt=\"SSAO using the normal maps.\" title=\"SSAO using the normal maps.\">\n</p>\n\nHere you see SSAO being used with the normal maps instead of the vertex normals.\nThis adds an extra level of detail and will pair nicely with the normal mapped lighting.\n\n```c\n    // ...\n\n    normal =\n      normalize\n        ( normalTex.rgb\n        * 2.0\n        - 1.0\n        );\n    normal =\n      normalize\n        ( mat3\n            ( tangent\n            , binormal\n            , vertexNormal\n            )\n        * normal\n        );\n\n    // ...\n```\n\nTo use the normal maps instead,\nyou'll need to transform the normal mapped normals from tangent space to view space\njust like you did in the lighting calculations.\n\n### Samples\n\nTo determine the amount of ambient occlusion for any particular fragment,\nyou'll need to sample the surrounding area.\nThe more samples you use, the better the approximation at the cost of performance.\n\n```cpp\n  // ...\n\n  for (int i = 0; i < numberOfSamples; ++i) {\n    LVecBase3f sample =\n      LVecBase3f\n        ( randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator)\n        ).normalized();\n\n    float rand = randomFloats(generator);\n    sample[0] *= rand;\n    sample[1] *= rand;\n    sample[2] *= rand;\n\n    float scale = (float) i / (float) numberOfSamples;\n    scale = lerp(0.1, 1.0, scale * scale);\n    sample[0] *= scale;\n    sample[1] *= scale;\n    sample[2] *= scale;\n\n    ssaoSamples.push_back(sample);\n  }\n\n  // ...\n```\n\nThe example code generates a number of random samples distributed in a hemisphere.\nThese `ssaoSamples` will be sent to the SSAO shader.\n\n```cpp\n    LVecBase3f sample =\n      LVecBase3f\n        ( randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        ).normalized();\n```\n\nIf you'd like to distribute your samples in a sphere instead,\nchange the random `z` component to range from negative one to one.\n\n### Noise\n\n```c\n  // ...\n\n  for (int i = 0; i < numberOfNoise; ++i) {\n    LVecBase3f noise =\n      LVecBase3f\n        ( randomFloats(generator) * 2.0 - 1.0\n        , randomFloats(generator) * 2.0 - 1.0\n        , 0.0\n        );\n\n    ssaoNoise.push_back(noise);\n  }\n\n  // ...\n```\n\nTo get a good sweep of the sampled area, you'll need to generate some noise vectors.\nThese noise vectors will randomly tilt the hemisphere around the current fragment.\n\n### Ambient Occlusion\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/KKt74VE.gif\" alt=\"SSAO Texture\" title=\"SSAO Texture\">\n</p>\n\nSSAO works by sampling the view space around a fragment.\nThe more samples that are below a surface, the darker the fragment color.\nThese samples are positioned at the fragment and pointed in the general direction of the vertex normal.\nEach sample is used to look up a position in the position framebuffer texture.\nThe position returned is compared to the sample.\nIf the sample is farther away from the camera than the position, the sample counts towards the fragment being occluded.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/Nm4CJDN.gif\" alt=\"SSAO Sampling\" title=\"SSAO Sampling\">\n</p>\n\nHere you see the space above the surface being sampled for occlusion.\n\n```c\n  // ...\n\n  float radius    = 1;\n  float bias      = 0.01;\n  float magnitude = 1.5;\n  float contrast  = 1.5;\n\n  // ...\n```\n\nLike some of the other techniques,\nthe SSAO shader has a few control knobs you can tweak to get the exact look you're going for.\nThe `bias` adds to the sample's distance from the camera.\nYou can use the bias to combat \"acne\".\nThe `radius` increases or decreases the coverage area of the sample space.\nThe `magnitude` either lightens or darkens the occlusion map.\nThe `contrast` either washes out or increases the starkness of the occlusion map.\n\n```c\n  // ...\n\n  vec4 position =           texture(positionTexture, texCoord);\n  vec3 normal   = normalize(texture(normalTexture,   texCoord).xyz);\n\n  int  noiseX = int(gl_FragCoord.x - 0.5) % 4;\n  int  noiseY = int(gl_FragCoord.y - 0.5) % 4;\n  vec3 random = noise[noiseX + (noiseY * 4)];\n\n  // ...\n```\n\nRetrieve the position, normal, and random vector for later use.\nRecall that the example code created a set number of random vectors.\nThe random vector is chosen based on the current fragment's screen position.\n\n```c\n  // ...\n\n  vec3 tangent  = normalize(random - normal * dot(random, normal));\n  vec3 binormal = cross(normal, tangent);\n  mat3 tbn      = mat3(tangent, binormal, normal);\n\n  // ...\n```\n\nUsing the random and normal vectors, assemble the tangent, binormal, and normal matrix.\nYou'll need this matrix to transform the sample vectors from tangent space to view space.\n\n```c\n  // ...\n\n  float occlusion = NUM_SAMPLES;\n\n  for (int i = 0; i < NUM_SAMPLES; ++i) {\n    // ...\n  }\n\n  // ...\n```\n\nWith the matrix in hand, the shader can now loop through the samples, subtracting how many are not occluded.\n\n```c\n    // ...\n\n    vec3 samplePosition = tbn * samples[i];\n         samplePosition = position.xyz + samplePosition * radius;\n\n    // ...\n```\n\nUsing the matrix, position the sample near the vertex/fragment position and scale it by the radius.\n\n```c\n    // ...\n\n    vec4 offsetUV      = vec4(samplePosition, 1.0);\n         offsetUV      = lensProjection * offsetUV;\n         offsetUV.xyz /= offsetUV.w;\n         offsetUV.xy   = offsetUV.xy * 0.5 + 0.5;\n\n    // ...\n```\n\nUsing the sample's position in view space, transform it from view space to clip space to UV space.\n\n```c\n-1 * 0.5 + 0.5 = 0\n 1 * 0.5 + 0.5 = 1\n```\n\nRecall that clip space components range from negative one to one and that UV coordinates range from zero to one.\nTo transform clip space coordinates to UV coordinates, multiply by one half and add one half.\n\n```c\n    // ...\n\n    vec4 offsetPosition = texture(positionTexture, offsetUV.xy);\n\n    float occluded = 0;\n    if (samplePosition.y + bias <= offsetPosition.y) { occluded = 0; } else { occluded = 1; }\n\n    // ...\n```\n\nUsing the offset UV coordinates,\ncreated by projecting the 3D sample onto the 2D position texture,\nfind the corresponding position vector.\nThis takes you from view space to clip space to UV space back to view space.\nThe shader takes this round trip to find out if some geometry is behind, at, or in front of this sample.\nIf the sample is in front of or at some geometry, this sample doesn't count towards the fragment being occluded.\nIf the sample is behind some geometry, this sample counts towards the fragment being occluded.\n\n```c\n    // ...\n\n    float intensity =\n      smoothstep\n        ( 0.0\n        , 1.0\n        ,   radius\n          / abs(position.y - offsetPosition.y)\n        );\n    occluded *= intensity;\n\n    occlusion -= occluded;\n\n    // ...\n```\n\nNow weight this sampled position by how far it is inside or outside the radius.\nFinally, subtract this sample from the occlusion factor since it assumes all of the samples are occluded before the loop.\n\n```c\n    // ...\n\n    occlusion /= NUM_SAMPLES;\n\n    // ...\n\n    fragColor = vec4(vec3(occlusion), position.a);\n\n    // ...\n```\n\nDivide the occluded count by the number of samples to scale the occlusion factor from `[0, NUM_SAMPLES]` to `[0, 1]`.\nZero means full occlusion and one means no occlusion.\nNow assign the occlusion factor to the fragment's color and you're done.\n\n```c\n    // ...\n\n    fragColor = vec4(vec3(occlusion), position.a);\n\n    // ...\n```\n\nFor the demo's purposes,\nthe example code sets the alpha channel to alpha channel of the position framebuffer texture to avoid covering up the background.\n\n### Blurring\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/QsqOhFR.gif\" alt=\"SSAO Blur Texture\" title=\"SSAO Blur Texture\">\n</p>\n\nThe SSAO framebuffer texture is noisy as is.\nYou'll want to blur it to remove the noise.\nRefer back to the section on [blurring](blur.md).\nFor the best results, use a median or Kuwahara filter to preserve the sharp edges.\n\n### Ambient Color\n\n```c\n  // ...\n\n  vec2 ssaoBlurTexSize  = textureSize(ssaoBlurTexture, 0).xy;\n  vec2 ssaoBlurTexCoord = gl_FragCoord.xy / ssaoBlurTexSize;\n  float ssao            = texture(ssaoBlurTexture, ssaoBlurTexCoord).r;\n\n  vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex * ssao;\n\n  // ...\n```\n\nThe final stop for SSAO is back in the lighting calculation.\nHere you see the occlusion factor being looked up in the\nSSAO framebuffer texture and then included in the ambient light calculation.\n\n### Source\n\n- [main.cxx](../demonstration/src/main.cxx)\n- [basic.vert](../demonstration/shaders/vertex/basic.vert)\n- [base.vert](../demonstration/shaders/vertex/base.vert)\n- [base.frag](../demonstration/shaders/fragment/base.frag)\n- [position.frag](../demonstration/shaders/fragment/position.frag)\n- [normal.frag](../demonstration/shaders/fragment/normal.frag)\n- [ssao.frag](../demonstration/shaders/fragment/ssao.frag)\n- [median-filter.frag](../demonstration/shaders/fragment/median-filter.frag)\n- [kuwahara-filter.frag](../demonstration/shaders/fragment/kuwahara-filter.frag)\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](bloom.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](motion-blur.md)\n"
  },
  {
    "path": "sections/texturing.md",
    "content": "[:arrow_backward:](render-to-texture.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](lighting.md)\n\n# 3D Game Shaders For Beginners\n\n## Texturing\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/cqbgT8b.gif\" alt=\"Diffuse Texture Only\" title=\"Diffuse Texture Only\">\n</p>\n\nTexturing involves mapping some color or some other kind of vector to a fragment using UV coordinates.\nBoth U and V range from zero to one.\nEach vertex gets a UV coordinate and this is outputted in the vertex shader.\n\n<p align=\"center\">\n<img src=\"https://i.imgur.com/JjAdNfk.png\" alt=\"UV Interpolation\" title=\"UV Interpolation\">\n</p>\n\nThe fragment shader receives the UV coordinate interpolated.\nInterpolated meaning the UV coordinate for the fragment is somewhere between the UV coordinates\nfor the vertexes that make up the triangle face.\n\n### Vertex\n\n```c\n#version 150\n\nuniform mat4 p3d_ModelViewProjectionMatrix;\n\nin vec2 p3d_MultiTexCoord0;\n\nin vec4 p3d_Vertex;\n\nout vec2 texCoord;\n\nvoid main()\n{\n  texCoord = p3d_MultiTexCoord0;\n\n  gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex;\n}\n```\n\nHere you see the vertex shader outputting the texture coordinate to the fragment shader.\nNotice how it's a two dimensional vector.\nOne dimension for U and one for V.\n\n### Fragment\n\n```c\n#version 150\n\nuniform sampler2D p3d_Texture0;\n\nin vec2 texCoord;\n\nout vec2 fragColor;\n\nvoid main()\n{\n  texColor = texture(p3d_Texture0, texCoord);\n\n  fragColor = texColor;\n}\n```\n\nHere you see the fragment shader looking up the color at its UV coordinate and outputting that as the fragment color.\n\n#### Screen Filled Texture\n\n```c\n#version 150\n\nuniform sampler2D screenSizedTexture;\n\nout vec2 fragColor;\n\nvoid main()\n{\n  vec2 texSize  = textureSize(texture, 0).xy;\n  vec2 texCoord = gl_FragCoord.xy / texSize;\n\n  texColor = texture(screenSizedTexture, texCoord);\n\n  fragColor = texColor;\n}\n```\n\nWhen performing render to texture, the mesh is a flat rectangle with the same aspect ratio as the screen.\nBecause of this, you can calculate the UV coordinates knowing only\nA) the width and height of the screen sized texture being UV mapped to the rectangle and\nB) the fragment's x and y coordinate.\nTo map x to U, divide x by the width of the input texture.\nSimilarly, to map y to V, divide y by the height of the input texture.\nYou'll see this technique used in the example code.\n\n## Copyright\n\n(C) 2019 David Lettier\n<br>\n[lettier.com](https://www.lettier.com)\n\n[:arrow_backward:](render-to-texture.md)\n[:arrow_double_up:](../README.md)\n[:arrow_up_small:](#)\n[:arrow_down_small:](#copyright)\n[:arrow_forward:](lighting.md)\n"
  }
]