LearnOpenGL

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

Parallax-Mapping.html (40699B)


      1 <!DOCTYPE html>
      2 <html lang="ja"> 
      3 <head>
      4     <meta charset="utf-8"/>
      5     <title>LearnOpenGL</title>
      6     <link rel="shortcut icon" type="image/ico" href="/favicon.ico"  />
      7 	<link rel="stylesheet" href="../static/style.css" />
      8 	<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script>
      9 	<script src="/static/functions.js"></script>
     10 </head>
     11 <body>
     12 	<nav>
     13 <ol>
     14 	<li id="Introduction">
     15 		<a href="https://learnopengl.com/Introduction">はじめに</a>
     16 	</li>
     17 	<li id="Getting-started">
     18 		<span class="closed">入門</span>
     19 		<ol>
     20 			<li id="Getting-started/OpenGL">
     21 				<a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a>
     22 			</li>
     23 			<li id="Getting-started/Creating-a-window">
     24 				<a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a>
     25 			</li>
     26 			<li id="Getting-started/Hello-Window">
     27 				<a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a>
     28 			</li>
     29 			<li id="Getting-started/Hello-Triangle">
     30 				<a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a>
     31 			</li>
     32 			<li id="Getting-started/Shaders">
     33 				<a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a>
     34 			</li>
     35 			<li id="Getting-started/Textures">
     36 				<a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a>
     37 			</li>
     38 			<li id="Getting-started/Transformations">
     39 				<a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a>
     40 			</li>
     41 			<li id="Getting-started/Coordinate-Systems">
     42 				<a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a>
     43 			</li>
     44 			<li id="Getting-started/Camera">
     45 				<a href="https://learnopengl.com/Getting-started/Camera">カメラ</a>
     46 			</li>
     47 			<li id="Getting-started/Review">
     48 				<a href="https://learnopengl.com/Getting-started/Review">まとめ</a>
     49 			</li>
     50 		</ol>
     51 	</li>
     52 	<li id="Lighting">
     53 		<span class="closed">Lighting </span>
     54 		<ol>
     55 			<li id="Lighting/Colors">
     56 				<a href="https://learnopengl.com/Lighting/Colors">Colors </a>
     57 			</li>
     58 			<li id="Lighting/Basic-Lighting">
     59 				<a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a>
     60 			</li>
     61 			<li id="Lighting/Materials">
     62 				<a href="https://learnopengl.com/Lighting/Materials">Materials </a>
     63 			</li>
     64 			<li id="Lighting/Lighting-maps">
     65 				<a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a>
     66 			</li>
     67 			<li id="Lighting/Light-casters">
     68 				<a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a>
     69 			</li>
     70 			<li id="Lighting/Multiple-lights">
     71 				<a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a>
     72 			</li>
     73 			<li id="Lighting/Review">
     74 				<a href="https://learnopengl.com/Lighting/Review">Review </a>
     75 			</li>
     76 		</ol>
     77 	</li>
     78 	<li id="Model-Loading">
     79 		<span class="closed">Model Loading </span>
     80 		<ol>
     81 			<li id="Model-Loading/Assimp">
     82 				<a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a>
     83 			</li>
     84 			<li id="Model-Loading/Mesh">
     85 				<a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a>
     86 			</li>
     87 			<li id="Model-Loading/Model">
     88 				<a href="https://learnopengl.com/Model-Loading/Model">Model </a>
     89 			</li>
     90 		</ol>
     91 	</li>
     92 	<li id="Advanced-OpenGL">
     93 		<span class="closed">Advanced OpenGL </span>
     94 		<ol>
     95 			<li id="Advanced-OpenGL/Depth-testing">
     96 				<a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a>
     97 			</li>
     98 			<li id="Advanced-OpenGL/Stencil-testing">
     99 				<a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a>
    100 			</li>
    101 			<li id="Advanced-OpenGL/Blending">
    102 				<a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a>
    103 			</li>
    104 			<li id="Advanced-OpenGL/Face-culling">
    105 				<a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a>
    106 			</li>
    107 			<li id="Advanced-OpenGL/Framebuffers">
    108 				<a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a>
    109 			</li>
    110 			<li id="Advanced-OpenGL/Cubemaps">
    111 				<a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a>
    112 			</li>
    113 			<li id="Advanced-OpenGL/Advanced-Data">
    114 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a>
    115 			</li>
    116 			<li id="Advanced-OpenGL/Advanced-GLSL">
    117 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a>
    118 			</li>
    119 			<li id="Advanced-OpenGL/Geometry-Shader">
    120 				<a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a>
    121 			</li>
    122 			<li id="Advanced-OpenGL/Instancing">
    123 				<a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a>
    124 			</li>
    125 			<li id="Advanced-OpenGL/Anti-Aliasing">
    126 				<a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a>
    127 			</li>
    128 		</ol>
    129 	</li>
    130 	<li id="Advanced-Lighting">
    131 		<span class="closed">Advanced Lighting </span>
    132 		<ol>
    133 			<li id="Advanced-Lighting/Advanced-Lighting">
    134 				<a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a>
    135 			</li>
    136 			<li id="Advanced-Lighting/Gamma-Correction">
    137 				<a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a>
    138 			</li>
    139 			<li id="Advanced-Lighting/Shadows">
    140 				<span class="closed">Shadows </span>
    141 				<ol>
    142 					<li id="Advanced-Lighting/Shadows/Shadow-Mapping">
    143 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a>
    144 					</li>
    145 					<li id="Advanced-Lighting/Shadows/Point-Shadows">
    146 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a>
    147 					</li>
    148 				</ol>
    149 			</li>
    150 			<li id="Advanced-Lighting/Normal-Mapping">
    151 				<a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a>
    152 			</li>
    153 			<li id="Advanced-Lighting/Parallax-Mapping">
    154 				<a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a>
    155 			</li>
    156 			<li id="Advanced-Lighting/HDR">
    157 				<a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a>
    158 			</li>
    159 			<li id="Advanced-Lighting/Bloom">
    160 				<a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a>
    161 			</li>
    162 			<li id="Advanced-Lighting/Deferred-Shading">
    163 				<a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a>
    164 			</li>
    165 			<li id="Advanced-Lighting/SSAO">
    166 				<a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a>
    167 			</li>
    168 		</ol>
    169 	</li>
    170 	<li id="PBR">
    171 		<span class="closed">PBR </span>
    172 		<ol>
    173 			<li id="PBR/Theory">
    174 				<a href="https://learnopengl.com/PBR/Theory">Theory </a>
    175 			</li>
    176 			<li id="PBR/Lighting">
    177 				<a href="https://learnopengl.com/PBR/Lighting">Lighting </a>
    178 			</li>
    179 			<li id="PBR/IBL">
    180 				<span class="closed">IBL </span>
    181 				<ol>
    182 					<li id="PBR/IBL/Diffuse-irradiance">
    183 						<a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a>
    184 					</li>
    185 					<li id="PBR/IBL/Specular-IBL">
    186 						<a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a>
    187 					</li>
    188 				</ol>
    189 			</li>
    190 		</ol>
    191 	</li>
    192 	<li id="In-Practice">
    193 		<span class="closed">In Practice </span>
    194 		<ol>
    195 			<li id="In-Practice/Debugging">
    196 				<a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a>
    197 			</li>
    198 			<li id="In-Practice/Text-Rendering">
    199 				<a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a>
    200 			</li>
    201 			<li id="In-Practice/2D-Game">
    202 				<span class="closed">2D Game </span>
    203 				<ol>
    204 					<li id="In-Practice/2D-Game/Breakout">
    205 						<a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a>
    206 					</li>
    207 					<li id="In-Practice/2D-Game/Setting-up">
    208 						<a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a>
    209 					</li>
    210 					<li id="In-Practice/2D-Game/Rendering-Sprites">
    211 						<a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a>
    212 					</li>
    213 					<li id="In-Practice/2D-Game/Levels">
    214 						<a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a>
    215 					</li>
    216 					<li id="In-Practice/2D-Game/Collisions">
    217 						<span class="closed">Collisions </span>
    218 						<ol>
    219 							<li id="In-Practice/2D-Game/Collisions/Ball">
    220 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a>
    221 							</li>
    222 							<li id="In-Practice/2D-Game/Collisions/Collision-detection">
    223 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a>
    224 							</li>
    225 							<li id="In-Practice/2D-Game/Collisions/Collision-resolution">
    226 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a>
    227 							</li>
    228 						</ol>
    229 					</li>
    230 					<li id="In-Practice/2D-Game/Particles">
    231 						<a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a>
    232 					</li>
    233 					<li id="In-Practice/2D-Game/Postprocessing">
    234 						<a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a>
    235 					</li>
    236 					<li id="In-Practice/2D-Game/Powerups">
    237 						<a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a>
    238 					</li>
    239 					<li id="In-Practice/2D-Game/Audio">
    240 						<a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a>
    241 					</li>
    242 					<li id="In-Practice/2D-Game/Render-text">
    243 						<a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a>
    244 					</li>
    245 					<li id="In-Practice/2D-Game/Final-thoughts">
    246 						<a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a>
    247 					</li>
    248 				</ol>
    249 			</li>
    250 		</ol>
    251 	</li>
    252 	<li id="Guest-Articles">
    253 		<span class="closed">Guest Articles </span>
    254 		<ol>
    255 			<li id="Guest-Articles/How-to-publish">
    256 				<a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a>
    257 			</li>
    258 			<li id="Guest-Articles/2020">
    259 				<span class="closed">2020 </span>
    260 				<ol>
    261 					<li id="Guest-Articles/2020/OIT">
    262 						<span class="closed">OIT </span>
    263 						<ol>
    264 							<li id="Guest-Articles/2020/OIT/Introduction">
    265 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a>
    266 							</li>
    267 							<li id="Guest-Articles/2020/OIT/Weighted-Blended">
    268 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a>
    269 							</li>
    270 						</ol>
    271 					</li>
    272 					<li id="Guest-Articles/2020/Skeletal-Animation">
    273 						<a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a>
    274 					</li>
    275 				</ol>
    276 			</li>
    277 			<li id="Guest-Articles/2021">
    278 				<span class="closed">2021 </span>
    279 				<ol>
    280 					<li id="Guest-Articles/2021/CSM">
    281 						<a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a>
    282 					</li>
    283 					<li id="Guest-Articles/2021/Scene">
    284 						<span class="closed">Scene </span>
    285 						<ol>
    286 							<li id="Guest-Articles/2021/Scene/Scene-Graph">
    287 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a>
    288 							</li>
    289 							<li id="Guest-Articles/2021/Scene/Frustum-Culling">
    290 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a>
    291 							</li>
    292 						</ol>
    293 					</li>
    294 					<li id="Guest-Articles/2021/Tessellation">
    295 						<span class="closed">Tessellation </span>
    296 						<ol>
    297 							<li id="Guest-Articles/2021/Tessellation/Height-map">
    298 								<a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a>
    299 							</li>
    300 						</ol>
    301 					</li>
    302 				</ol>
    303 			</li>
    304 		</ol>
    305 	</li>
    306 	<li id="Code-repository">
    307 		<a href="https://learnopengl.com/Code-repository">Code repository </a>
    308 	</li>
    309 	<li id="Translations">
    310 		<a href="https://learnopengl.com/Translations">Translations </a>
    311 	</li>
    312 	<li id="About">
    313 		<a href="https://learnopengl.com/About">About </a>
    314 	</li>
    315 </ol>
    316 	</nav>
    317 	<main>
    318     <h1 id="content-title">Parallax Mapping</h1>
    319 <h1 id="content-url" style='display:none;'>Advanced-Lighting/Parallax-Mapping</h1>
    320 <p>
    321   Parallax mapping is a technique similar to normal mapping, but based on different principles. Just like normal mapping it is a technique that significantly boosts a textured surface's detail and gives it a sense of depth. While also an illusion, parallax mapping is a lot better in conveying a sense of depth and together with normal mapping gives incredibly realistic results. While parallax mapping isn't necessarily a technique directly related to (advanced) lighting, I'll still discuss it here as the technique is a logical follow-up of normal mapping. Note that getting an understanding of normal mapping, specifically tangent space, is strongly advised before learning parallax mapping.
    322 </p>
    323 
    324 <p>
    325   Parallax mapping is closely related to the family of <def>displacement mapping</def> techniques that <em>displace</em> or <em>offset</em> vertices based on geometrical information stored inside a texture. One way to do this, is to take a plane with roughly 1000 vertices and displace each of these vertices based on a value in a texture that tells us the height of the plane at that specific area. Such a texture that contains height values per texel is called a <def>height map</def>. An example height map derived from the geometric properties of a simple brick surface looks a bit like this:
    326 </p>
    327 
    328 <img src="/img/advanced-lighting/parallax_mapping_height_map.png" alt="Height map used in OpenGL for parallax mapping"/>
    329   
    330 <p>
    331  When spanned over a plane, each vertex is displaced based on the sampled height value in the height map, transforming a flat plane to a rough bumpy surface based on a material's geometric properties. For instance, taking a flat plane displaced with the above heightmap results in the following image:
    332 </p>
    333   
    334   <img src="/img/advanced-lighting/parallax_mapping_plane_heightmap.png" class="clean" alt="Height map applied to simple plane"/>
    335     
    336 <p>
    337   A problem with displacing vertices this way is that a plane needs to contain a huge amount of triangles to get a realistic displacement, otherwise the displacement looks too blocky. As each flat surface may then require over 10000 vertices this quickly becomes computationally infeasible. What if we could somehow achieve similar realism without the need of extra vertices? In fact, what if I were to tell you that the previously shown displaced surface is actually rendered with only 2 triangles. This brick surface shown is rendered with <def>parallax mapping</def>, a displacement mapping technique that doesn't require extra vertex data to convey depth, but (similar to normal mapping) uses a clever technique to trick the user.
    338 </p>
    339     
    340 <p>
    341   The idea behind parallax mapping is to alter the texture coordinates in such a way that it looks like a fragment's surface is higher or lower than it actually is, all based on the view direction and a heightmap. To understand how it works, take a look at the following image of our brick surface:
    342  </p>
    343     
    344     <img src="/img/advanced-lighting/parallax_mapping_plane_height.png" class="clean" alt="Diagram of how parallax mapping works in OpenGL"/>
    345       
    346 <p>
    347   Here the rough red line represents the values in the heightmap as the geometric surface representation of the brick surface and the vector \(\color{orange}{\bar{V}}\) represents the surface to view direction (<var>viewDir</var>). If the plane would have actual displacement, the viewer would see the surface at point \(\color{blue}B\). However, as our plane has no actual displacement the view direction is calculated from point \(\color{green}A\) as we'd expect. Parallax mapping aims to offset the texture coordinates at fragment position \(\color{green}A\) in such a way that we get texture coordinates at point \(\color{blue}B\). We then use the texture coordinates at point \(\color{blue}B\) for all subsequent texture samples, making it look like the viewer is actually looking at point \(\color{blue}B\).
    348 </p>
    349       
    350 <p>
    351   The trick is to figure out how to get the texture coordinates at point \(\color{blue}B\) from point \(\color{green}A\). Parallax mapping tries to solve this by scaling the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) by the height at fragment \(\color{green}A\). So we're scaling the length of \(\color{orange}{\bar{V}}\) to be equal to a sampled value from the heightmap \(\color{green}{H(A)}\) at fragment position \(\color{green}A\). The image below shows this scaled vector \(\color{brown}{\bar{P}}\):
    352 </p>
    353       
    354       <img src="/img/advanced-lighting/parallax_mapping_scaled_height.png" class="clean" alt="Diagram of how parallax mapping works in OpenGL with vector scaled by fragment's height."/>
    355         
    356 <p>
    357   We then take this vector \(\color{brown}{\bar{P}}\) and take its vector coordinates that align with the plane as the texture coordinate offset. This works because vector \(\color{brown}{\bar{P}}\) is calculated using a height value from the heightmap. So the higher a fragment's height, the more it effectively gets displaced. 
    358 </p>
    359         
    360 <p>
    361   This little trick gives good results most of the time, but it is still a really crude approximation to get to point \(\color{blue}B\). When heights change rapidly over a surface the results tend to look unrealistic as the vector \(\color{brown}{\bar{P}}\) will not end up close to \(\color{blue}B\) as you can see below:
    362 </p>
    363         
    364         <img src="/img/advanced-lighting/parallax_mapping_incorrect_p.png" class="clean" alt="Diagram of why basic parallax mapping gives incorrect result at steep height changes."/>
    365         
    366 <p>
    367   Another issue with parallax mapping is that it's difficult to figure out which coordinates to retrieve from \(\color{brown}{\bar{P}}\) when the surface is arbitrarily rotated in some way. We'd rather do this in a different coordinate space where the <code>x</code> and <code>y</code> component of vector \(\color{brown}{\bar{P}}\) always align with the texture's surface. If you've followed along in the <a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping"  target="_blank">normal mapping</a> chapter you probably guessed how we can accomplish this. And yes, we would like to do parallax mapping in tangent space.
    368 </p>
    369           
    370 <p>
    371   By transforming the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) to tangent space, the transformed \(\color{brown}{\bar{P}}\) vector will have its <code>x</code> and <code>y</code> component aligned to the surface's tangent and bitangent vectors. As the tangent and bitangent vectors are pointing in the same direction as the surface's texture coordinates we can take the <code>x</code> and <code>y</code> components of \(\color{brown}{\bar{P}}\) as the texture coordinate offset, regardless of the surface's orientation.
    372 </p>
    373           
    374 <p>
    375   But enough about the theory, let's get our feet wet and start implementing actual parallax mapping.
    376 </p>
    377           
    378 <h2>Parallax mapping</h2>
    379 <p>
    380   For parallax mapping we're going to use a simple 2D plane for which we calculated its tangent and bitangent vectors before sending it to the GPU; similar to what we did in the normal mapping chapter. Onto the plane we're going to attach a <a href="/img/textures/bricks2.jpg" target="_blank">diffuse texture</a>, a <a href="/img/textures/bricks2_normal.jpg" target="_blank">normal map</a>, and a <a href="/img/textures/bricks2_disp.jpg" target="_blank">displacement map</a> that you can download from their urls. For this example we're going to use parallax mapping in conjunction with normal mapping. Because parallax mapping gives the illusion of displacing a surface, the illusion breaks when the lighting doesn't match. As normal maps are often generated from heightmaps, using a normal map together with the heightmap makes sure the lighting is in place with the displacement.
    381 </p>
    382           
    383 <p>
    384   You may have already noted that the displacement map linked above is the inverse of the heightmap shown at the start of this chapter. With parallax mapping it makes more sense to use the inverse of the heightmap as it's easier to fake depth than height on flat surfaces. This slightly changes how we perceive parallax mapping as shown below:
    385 </p>
    386           
    387 <img src="/img/advanced-lighting/parallax_mapping_depth.png" class="clean" alt="Parallax mapping using a depth map instead of a heightmap"/>
    388   
    389 <p>
    390   We again have a points \(\color{green}A\) and \(\color{blue}B\), but this time we obtain vector \(\color{brown}{\bar{P}}\)  by <strong>subtracting</strong> vector \(\color{orange}{\bar{V}}\) from the texture coordinates at point \(\color{green}A\). We can obtain depth values instead of height values by subtracting the sampled heightmap values from <code>1.0</code> in the shaders, or by simply inversing its texture values in image-editing software as we did with the depthmap linked above.
    391 </p>
    392           
    393           
    394 <p>
    395   Parallax mapping is implemented in the fragment shader as the displacement effect is different all over a triangle's surface. In the fragment shader we're then going to need to calculate the fragment-to-view direction vector \(\color{orange}{\bar{V}}\) so we need the view position and a fragment position in tangent space. In the normal mapping chapter we already had a vertex shader that sends these vectors in tangent space so we can take an exact copy of that chapter's vertex shader:
    396 </p>
    397           
    398 <pre><code>
    399 #version 330 core
    400 layout (location = 0) in vec3 aPos;
    401 layout (location = 1) in vec3 aNormal;
    402 layout (location = 2) in vec2 aTexCoords;
    403 layout (location = 3) in vec3 aTangent;
    404 layout (location = 4) in vec3 aBitangent;
    405 
    406 out VS_OUT {
    407     vec3 FragPos;
    408     vec2 TexCoords;
    409     vec3 TangentLightPos;
    410     vec3 TangentViewPos;
    411     vec3 TangentFragPos;
    412 } vs_out;
    413 
    414 uniform mat4 projection;
    415 uniform mat4 view;
    416 uniform mat4 model;
    417 
    418 uniform vec3 lightPos;
    419 uniform vec3 viewPos;
    420 
    421 void main()
    422 {
    423     gl_Position      = projection * view * model * vec4(aPos, 1.0);
    424     vs_out.FragPos   = vec3(model * vec4(aPos, 1.0));   
    425     vs_out.TexCoords = aTexCoords;    
    426     
    427     vec3 T   = normalize(mat3(model) * aTangent);
    428     vec3 B   = normalize(mat3(model) * aBitangent);
    429     vec3 N   = normalize(mat3(model) * aNormal);
    430     mat3 TBN = transpose(mat3(T, B, N));
    431 
    432     vs_out.TangentLightPos = TBN * lightPos;
    433     vs_out.TangentViewPos  = TBN * viewPos;
    434     vs_out.TangentFragPos  = TBN * vs_out.FragPos;
    435 }   
    436 </code></pre>
    437 
    438 <p>
    439   Within the fragment shader we then implement the parallax mapping logic. The fragment shader looks a bit like this:
    440 </p>
    441           
    442 <pre><code>
    443 #version 330 core
    444 out vec4 FragColor;
    445 
    446 in VS_OUT {
    447     vec3 FragPos;
    448     vec2 TexCoords;
    449     vec3 TangentLightPos;
    450     vec3 TangentViewPos;
    451     vec3 TangentFragPos;
    452 } fs_in;
    453 
    454 uniform sampler2D diffuseMap;
    455 uniform sampler2D normalMap;
    456 uniform sampler2D depthMap;
    457   
    458 uniform float height_scale;
    459   
    460 vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir);
    461   
    462 void main()
    463 {           
    464     // offset texture coordinates with Parallax Mapping
    465     vec3 viewDir   = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
    466     vec2 texCoords = ParallaxMapping(fs_in.TexCoords,  viewDir);
    467 
    468     // then sample textures with new texture coords
    469     vec3 diffuse = texture(diffuseMap, texCoords);
    470     vec3 normal  = texture(normalMap, texCoords);
    471     normal = normalize(normal * 2.0 - 1.0);
    472     // proceed with lighting code
    473     [...]    
    474 }
    475   
    476 </code></pre>
    477           
    478 <p>
    479   We defined a function called <fun>ParallaxMapping</fun> that takes as input the fragment's texture coordinates and the fragment-to-view direction \(\color{orange}{\bar{V}}\) in tangent space. The function returns the displaced texture coordinates. We then use these <em>displaced</em> texture coordinates as the texture coordinates for sampling the diffuse and normal map. As a result, the fragment's diffuse and normal vector correctly corresponds to the surface's displaced geometry.
    480 </p>
    481           
    482 <p>
    483   Let's take a look inside the <fun>ParallaxMapping</fun> function:
    484 </p>
    485           
    486 <pre><code>
    487 vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
    488 { 
    489     float height =  texture(depthMap, texCoords).r;    
    490     vec2 p = viewDir.xy / viewDir.z * (height * height_scale);
    491     return texCoords - p;    
    492 } 
    493 </code></pre>
    494   
    495 <p>
    496   This relatively simple function is a direct translation of what we've discussed so far. We take the original texture coordinates <var>texCoords</var> and use these to sample the height (or depth) from the <var>depthMap</var> at the current fragment \(\color{green}{A}\) as \(\color{green}{H(A)}\). We then calculate \(\color{brown}{\bar{P}}\) as the <code>x</code> and <code>y</code> component of the tangent-space <var>viewDir</var> vector divided by its <code>z</code> component and scaled by \(\color{green}{H(A)}\). We also introduced a <var>height_scale</var> uniform for some extra control as the parallax effect is usually too strong without an extra scale parameter. We then subtract this vector \(\color{brown}{\bar{P}}\) from the texture coordinates to get the final displaced texture coordinates.
    497   </p>
    498   
    499 <p>
    500   What is interesting to note here is the division of <var>viewDir.xy</var> by <var>viewDir.z</var>. As the <var>viewDir</var> vector is normalized, <var>viewDir.z</var> will be somewhere in the range between <code>0.0</code> and <code>1.0</code>. When <var>viewDir</var> is largely parallel to the surface, its <code>z</code> component is close to <code>0.0</code> and the division returns a much larger vector \(\color{brown}{\bar{P}}\) compared to when <var>viewDir</var> is largely perpendicular to the surface. We're adjusting the size of \(\color{brown}{\bar{P}}\) in such a way that it offsets the texture coordinates at a larger scale when looking at a surface from an angle compared to when looking at it from the top; this gives more realistic results at angles. <br/>
    501   Some prefer to leave the division by <var>viewDir.z</var> out of the equation as default Parallax Mapping could produce undesirable results at angles; the technique is then called <def>Parallax Mapping with Offset Limiting</def>. Choosing which technique to pick is usually a matter of personal preference.
    502 </p>
    503     
    504 <p>
    505   The resulting texture coordinates are then used to sample the other textures (diffuse and normal) and this gives a very neat displaced effect as you can see below with a <var>height_scale</var> of roughly <code>0.1</code>:
    506 </p>
    507   
    508 <img src="/img/advanced-lighting/parallax_mapping.png" alt="Image of parallax mapping in OpenGL"/>
    509     
    510 <p>
    511   Here you can see the difference between normal mapping and parallax mapping combined with normal mapping. Because parallax mapping tries to simulate depth it is actually possible to have bricks overlap other bricks based on the direction you view them. 
    512 </p>
    513     
    514 <p>
    515   You can still see a few weird border artifacts at the edge of the parallax mapped plane. This happens because at the edges of the plane the displaced texture coordinates can oversample outside the range [<code>0</code>, <code>1</code>]. This gives unrealistic results based on the texture's wrapping mode(s). A cool trick to solve this issue is to discard the fragment whenever it samples outside the default texture coordinate range:
    516 </p>
    517                   
    518 <pre><code>
    519 texCoords = ParallaxMapping(fs_in.TexCoords,  viewDir);
    520 if(texCoords.x &gt; 1.0 || texCoords.y &gt; 1.0 || texCoords.x &lt; 0.0 || texCoords.y &lt; 0.0)
    521     discard;
    522 </code></pre>
    523                 
    524 <p>
    525   All fragments with (displaced) texture coordinates outside the default range are discarded and Parallax Mapping then gives proper result around the edges of a surface. Note that this trick doesn't work on all types of surfaces, but when applied to a plane it gives great results:
    526 </p>
    527   
    528   <img src="/img/advanced-lighting/parallax_mapping_edge_fix.png" class="clean" alt="Parallax mapping with fragments discarded at the borders, fixing edge artifacts in OpenGL"/>
    529   
    530 <p>
    531   You can find the source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.1.parallax_mapping/parallax_mapping.cpp" target="_blank">here</a>.
    532 </p>
    533           
    534 <p>
    535   It looks great and is quite fast as well as we only need a single extra texture sample for parallax mapping to work. It does come with a few issues though as it sort of breaks down when looking at it from an angle (similar to normal mapping) and gives incorrect results with steep height changes, as you can see below:
    536 </p>
    537     
    538     <img src="/img/advanced-lighting/parallax_mapping_issues.png" alt="Three images displaying the issues with standard parallax mapping: breaks down at angles and incorrect results with steep height changes."/>
    539       
    540 <p>
    541   The reason that it doesn't work properly at times is that it's just a crude approximation of displacement mapping. There are some extra tricks however that still allows us to get almost perfect results with steep height changes, even when looking at an angle. For instance, what if we instead of one sample take multiple samples to find the closest point to \(\color{blue}B\)?
    542 </p>
    543       
    544 <h2>Steep Parallax Mapping</h2>
    545 <p>
    546   Steep Parallax Mapping is an extension on top of Parallax Mapping in that it uses the same principles, but instead of 1 sample it takes multiple samples to better pinpoint vector \(\color{brown}{\bar{P}}\) to \(\color{blue}B\). This gives much better results, even with steep height changes, as the accuracy of the technique is improved by the number of samples.
    547 </p>
    548       
    549 <p>
    550   The general idea of Steep Parallax Mapping is that it divides the total depth range into multiple layers of the same height/depth. For each of these layers we sample the depthmap, shifting the texture coordinates along the direction of \(\color{brown}{\bar{P}}\), until we find a sampled depth value that is less than the depth value of the current layer. Take a look at the following image:
    551 </p>
    552       
    553       <img src="/img/advanced-lighting/parallax_mapping_steep_parallax_mapping_diagram.png" class="clean" alt="Diagram of how steep Parallax Mapping works in OpenGL"/>
    554         
    555 <p>
    556   We traverse the depth layers from the top down and for each layer we compare its depth value to the depth value stored in the depthmap. If the layer's depth value is less than the depthmap's value it means this layer's part of vector \(\color{brown}{\bar{P}}\) is not below the surface. We continue this process until the layer's depth is higher than the value stored in the depthmap: this point is then below the (displaced) geometric surface. 
    557 </p>
    558         
    559 <p>
    560   In this example we can see that the depthmap value at the second layer (D(2) = 0.73) is lower than the second layer's depth value <code>0.4</code> so we continue. In the next iteration, the layer's depth value <code>0.6</code> is higher than the depthmap's sampled depth value (D(3) = 0.37). We can thus assume vector \(\color{brown}{\bar{P}}\) at the third layer to be the most viable position of the displaced geometry. We then take the texture coordinate offset \(T_3\) from vector \(\color{brown}{\bar{P_3}}\) to displace the fragment's texture coordinates. You can see how the accuracy increases with more depth layers. 
    561 </p>
    562         
    563 <p>
    564   To implement this technique we only have to change the <fun>ParallaxMapping</fun> function as we already have all the variables we need:
    565 </p>
    566         
    567 <pre><code>
    568 vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
    569 { 
    570     // number of depth layers
    571     const float numLayers = 10;
    572     // calculate the size of each layer
    573     float layerDepth = 1.0 / numLayers;
    574     // depth of current layer
    575     float currentLayerDepth = 0.0;
    576     // the amount to shift the texture coordinates per layer (from vector P)
    577     vec2 P = viewDir.xy * height_scale; 
    578     vec2 deltaTexCoords = P / numLayers;
    579   
    580     [...]     
    581 }   
    582 </code></pre>
    583         
    584 <p>
    585   Here we first set things up: we specify the number of layers, calculate the depth offset of each layer, and finally calculate the texture coordinate offset that we have to shift along the direction of \(\color{brown}{\bar{P}}\) per layer.
    586 </p>
    587         
    588 <p>
    589   We then iterate through all the layers, starting from the top, until we find a depthmap value less than the layer's depth value:
    590 </p>
    591         
    592 <pre><code>
    593 // get initial values
    594 vec2  currentTexCoords     = texCoords;
    595 float currentDepthMapValue = texture(depthMap, currentTexCoords).r;
    596   
    597 while(currentLayerDepth &lt; currentDepthMapValue)
    598 {
    599     // shift texture coordinates along direction of P
    600     currentTexCoords -= deltaTexCoords;
    601     // get depthmap value at current texture coordinates
    602     currentDepthMapValue = texture(depthMap, currentTexCoords).r;  
    603     // get depth of next layer
    604     currentLayerDepth += layerDepth;  
    605 }
    606 
    607 return currentTexCoords;
    608 </code></pre>
    609         
    610 <p>
    611   Here we loop over each depth layer and stop until we find the texture coordinate offset along vector \(\color{brown}{\bar{P}}\) that first returns a depth that's below the (displaced) surface. The resulting offset is subtracted from the fragment's texture coordinates to get a final displaced texture coordinate vector, this time with much more accuracy compared to traditional parallax mapping.
    612 </p>
    613         
    614 <p>
    615   With around <code>10</code> samples the brick surface already looks more viable even when looking at it from an angle, but steep parallax mapping really shines when having a complex surface with steep height changes; like the earlier displayed wooden toy surface:
    616 </p>
    617         
    618         <img src="/img/advanced-lighting/parallax_mapping_steep_parallax_mapping.png" class="clean" alt="Steep Parallax Mapping implemented in OpenGL"/>
    619           
    620 <p>
    621   We can improve the algorithm a bit by exploiting one of Parallax Mapping's properties. When looking straight onto a surface there isn't much texture displacement going on while there is a lot of displacement when looking at a surface from an angle (visualize the view direction on both cases). By taking less samples when looking straight at a surface and more samples when looking at an angle we only sample the necessary amount:
    622 </p>
    623           
    624 <pre><code>
    625 const float minLayers = 8.0;
    626 const float maxLayers = 32.0;
    627 float numLayers = mix(maxLayers, minLayers, max(dot(vec3(0.0, 0.0, 1.0), viewDir), 0.0));  
    628 </code></pre>
    629           
    630 <p>
    631   Here we take the dot product of <var>viewDir</var> and the positive z direction and use its result to align the number of samples to <var>minLayers</var> or <var>maxLayers</var> based on the angle we're looking towards a surface (note that the positive z direction equals the surface's normal vector in tangent space). If we were to look at a direction parallel to the surface we'd use a total of <code>32</code> layers.
    632 </p>
    633           
    634 <p>
    635   You can find the updated source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.2.steep_parallax_mapping/steep_parallax_mapping.cpp" target="_blank">here</a>. You can also find the wooden toy box surface here: <a href="/img/textures/wood.png" target="_blank">diffuse</a>, <a href="/img/textures/toy_box_normal.png" target="_blank">normal</a> and <a href="/img/textures/toy_box_disp.png" target="_blank">depth</a>.
    636 </p>
    637           
    638 <p>
    639   Steep Parallax Mapping also comes with its problems though. Because the technique is based on a finite number of samples, we get aliasing effects and the clear distinctions between layers can easily be spotted:
    640 </p>
    641           
    642           <img src="/img/advanced-lighting/parallax_mapping_steep_artifact.png"  class="clean" alt="The visible layers of Steep Parallax Mapping can easily be detected with small numbers"/>
    643             
    644 <p>
    645   We can reduce the issue by taking a larger number of samples, but this quickly becomes too heavy a burden on performance. There are several approaches that aim to fix this issue by not taking the first position that's below the (displaced) surface, but by <em>interpolating</em> between the position's two closest depth layers to find a much closer match to \(\color{blue}B\). 
    646 </p>
    647             
    648 <p>
    649   Two of the more popular of these approaches are called <def>Relief Parallax Mapping</def> and <def>Parallax Occlusion Mapping</def> of which Relief Parallax Mapping gives the most accurate results, but is also more performance heavy compared to Parallax Occlusion Mapping. Because Parallax Occlusion Mapping gives almost the same results as Relief Parallax Mapping and is also more efficient it is often the preferred approach.
    650 </p>
    651             
    652 <h2>Parallax Occlusion Mapping</h2>
    653 <p>
    654     Parallax Occlusion Mapping is based on the same principles as Steep Parallax Mapping, but instead of taking the texture coordinates of the first depth layer after a collision, we're going to linearly interpolate between the depth layer after and before the collision. We base the weight of the linear interpolation on how far the surface's height is from the depth layer's value of both layers. Take a look at the following picture to get a grasp of how it works:
    655 </p>
    656             
    657 <img src="/img/advanced-lighting/parallax_mapping_parallax_occlusion_mapping_diagram.png" class="clean" alt="How Parallax Occlusion Mapping works in OpenGL"/>
    658               
    659 <p>
    660   As you can see, it's largely similar to Steep Parallax Mapping with as an extra step the linear interpolation between the two depth layers' texture coordinates surrounding the intersected point. This is again an approximation, but significantly more accurate than Steep Parallax Mapping.
    661 </p>
    662               
    663 <p>
    664   The code for Parallax Occlusion Mapping is an extension on top of Steep Parallax Mapping and not too difficult:
    665 </p>
    666               
    667 <pre><code>
    668 [...] // steep parallax mapping code here
    669   
    670 // get texture coordinates before collision (reverse operations)
    671 vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
    672 
    673 // get depth after and before collision for linear interpolation
    674 float afterDepth  = currentDepthMapValue - currentLayerDepth;
    675 float beforeDepth = texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth;
    676  
    677 // interpolation of texture coordinates
    678 float weight = afterDepth / (afterDepth - beforeDepth);
    679 vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
    680 
    681 return finalTexCoords;  
    682 </code></pre>
    683               
    684 <p>
    685   After we found the depth layer after intersecting the (displaced) surface geometry, we also retrieve the texture coordinates of the depth layer before intersection. Then we calculate the distance of the  (displaced) geometry's depth from the corresponding depth layers and interpolate between these two values. The linear interpolation is a basic interpolation between both layer's texture coordinates. The function then finally returns the final interpolated texture coordinates.
    686 </p>
    687               
    688 <p>
    689   Parallax Occlusion Mapping gives surprisingly good results and although some slight artifacts and aliasing issues are still visible, it's a generally a good trade-off and only really visible when heavily zoomed in or looking at very steep angles. 
    690 </p>
    691               
    692               <img src="/img/advanced-lighting/parallax_mapping_parallax_occlusion_mapping.png" alt="Image of Parallax Occlusion Mapping in OpenGL"/>                                 
    693                  
    694 <p>
    695  You can find the source code <a href="/code_viewer_gh.php?code=src/5.advanced_lighting/5.3.parallax_occlusion_mapping/parallax_occlusion_mapping.cpp" target="_blank">here</a>.
    696 </p>
    697                     
    698 <p>
    699   Parallax Mapping is a great technique to boost the detail of your scene, but does come with a few artifacts you'll have to consider when using it. Most often, parallax mapping is used on floor or wall-like surfaces where it's not as easy to determine the surface's outline and the viewing angle is most often roughly perpendicular to the surface. This way, the artifacts of Parallax Mapping aren't as noticeable and make it an incredibly interesting technique for boosting your objects' details. 
    700 </p>            
    701                 
    702 <h2>Additional resources</h2>
    703 <ul>
    704      <li><a href="http://sunandblackcat.com/tipFullView.php?topicid=28" target="_blank">Parallax Occlusion Mapping in GLSL</a>: great parallax mapping tutorial by sunandblackcat.com.</li>
    705     <li><a href="https://www.youtube.com/watch?v=xvOT62L-fQI" target="_blank">How Parallax Displacement Mapping Works</a>: a nice video tutorial of how parallax mapping works by TheBennyBox.</li>
    706 </ul>       
    707 
    708     </div>
    709     
    710     <div id="hover">
    711         HI
    712     </div>
    713    <!-- 728x90/320x50 sticky footer -->
    714 <div id="waldo-tag-6196"></div>
    715 
    716    <div id="disqus_thread"></div>
    717 
    718     
    719 
    720 
    721 </div> <!-- container div -->
    722 
    723 
    724 </div> <!-- super container div -->
    725 </body>
    726 </html>
    727 	</main>
    728 </body>
    729 </html>