LearnOpenGL

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

Rendering-Sprites.html (25648B)


      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">Rendering Sprites</h1>
    319 <h1 id="content-url" style='display:none;'>In-Practice/2D-Game/Rendering-Sprites</h1>
    320 <p>
    321   To bring some life to the currently black abyss of our game world, we will render sprites to fill the void. A <def>sprite</def> has many definitions, but it's effectively not much more than a 2D image used together with some data to position it in a larger world (e.g. position, rotation, and size). Basically, sprites are the render-able image/texture objects we use in a 2D game.
    322 </p>
    323 
    324 <p>
    325   We can, just like we did in previous chapters, create a 2D shape out of vertex data, pass all data to the GPU, and transform it all by hand. However, in a larger application like this we rather have some abstractions on rendering 2D shapes. If we were to manually define these shapes and transformations for each object, it'll quickly get messy. 
    326 </p>
    327 
    328 <p>
    329   In this chapter we'll define a rendering class that allows us to render a large amount of unique sprites with a minimal amount of code. This way, we're abstracting the gameplay code from the gritty OpenGL rendering code as is commonly done in larger projects. First, we have to set up a proper projection matrix though.
    330 </p>
    331 
    332 <h2>2D projection matrix</h2>
    333 <p>
    334   We know from the <a href="https://learnopengl.com/Getting-started/Coordinate-Systems" target="_blank">coordinate systems</a> chapter that a projection matrix converts all view-space coordinates to clip-space (and then to normalized device) coordinates. By generating the appropriate projection matrix we can  work with different coordinates that are easier to work with, compared to directly specifying all coordinates as normalized device coordinates.  
    335 </p>
    336 
    337 <p>
    338   We don't need any perspective applied to the coordinates, since the game is entirely in 2D, so an orthographic projection matrix would suit the rendering quite well. Because an orthographic projection matrix directly transforms all coordinates to normalized device coordinates, we can choose to specify the world coordinates as screen coordinates by defining the projection matrix as follows:
    339 </p>
    340 
    341 <pre><code>
    342 glm::mat4 projection = <function id='59'>glm::ortho</function>(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f);  
    343 </code></pre>
    344 
    345 <p>
    346   The first four arguments specify in order the left, right, bottom, and top part of the projection frustum. This projection matrix transforms all <code>x</code> coordinates between <code>0</code> and <code>800</code> to <code>-1</code> and <code>1</code>, and all <code>y</code> coordinates between <code>0</code> and <code>600</code> to <code>-1</code> and <code>1</code>. Here we specified that the top of the frustum has a <code>y</code> coordinate of <code>0</code>, while the bottom has a <code>y</code> coordinate of <code>600</code>. The result is that the top-left coordinate of the scene will be at (<code>0,0</code>) and the bottom-right part of the screen is at coordinate (<code>800,600</code>), just like screen coordinates; the world-space coordinates directly correspond to the resulting pixel coordinates.
    347 </p>
    348 
    349 <img src="/img/in-practice/breakout/projection.png" class="clean" alt="Orthographic projection in OpenGL"/>
    350 
    351 <p>
    352   This allows us to specify all vertex coordinates equal to the pixel coordinates they end up in on the screen, which is rather intuitive for 2D games.
    353 </p>
    354 
    355 <h2>Rendering sprites</h2>
    356 <p>
    357   Rendering an actual sprite shouldn't be too complicated. We create a textured quad that we can transform with a model matrix, after which we project it using the previously defined orthographic projection matrix.
    358 </p>
    359 
    360 <note>
    361   Since Breakout is a single-scene game, there is no need for a view/camera matrix. Using the projection matrix we can directly transform the world-space coordinates to normalized device coordinates.
    362 </note>
    363 
    364 <p>
    365   To transform a sprite, we use the following vertex shader:
    366 </p>
    367 
    368 <pre><code>
    369 #version 330 core
    370 layout (location = 0) in vec4 vertex; // &lt;vec2 position, vec2 texCoords&gt;
    371 
    372 out vec2 TexCoords;
    373 
    374 uniform mat4 model;
    375 uniform mat4 projection;
    376 
    377 void main()
    378 {
    379     TexCoords = vertex.zw;
    380     gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
    381 }
    382 </code></pre>
    383 
    384 <p>
    385   Note that we store both the position and texture-coordinate data in a single <fun>vec4</fun> variable. Because both the position and texture coordinates contain two floats, we can combine them in a single vertex attribute.
    386 </p>
    387 
    388 <p>
    389   The fragment shader is relatively straightforward as well. We take a texture and a color vector that both affect the final color of the fragment. By having a uniform color vector, we can easily change the color of sprites from the game-code:
    390 </p>
    391 
    392 <pre><code>
    393 #version 330 core
    394 in vec2 TexCoords;
    395 out vec4 color;
    396 
    397 uniform sampler2D image;
    398 uniform vec3 spriteColor;
    399 
    400 void main()
    401 {    
    402     color = vec4(spriteColor, 1.0) * texture(image, TexCoords);
    403 }  
    404 </code></pre>
    405 
    406 <p>
    407   To make the rendering of sprites more organized, we define a <fun>SpriteRenderer</fun> class that is able to render a sprite with just a single function. Its definition is as follows:
    408 </p>
    409 
    410 <pre><code>
    411 class SpriteRenderer
    412 {
    413     public:
    414         SpriteRenderer(Shader &shader);
    415         ~SpriteRenderer();
    416 
    417         void DrawSprite(Texture2D &texture, glm::vec2 position, 
    418             glm::vec2 size = glm::vec2(10.0f, 10.0f), float rotate = 0.0f, 
    419             glm::vec3 color = glm::vec3(1.0f));
    420     private:
    421         Shader       shader; 
    422         unsigned int quadVAO;
    423 
    424         void initRenderData();
    425 };
    426 </code></pre>
    427 
    428 <p>
    429   The <def>SpriteRenderer</def> class hosts a shader object, a single vertex array object, and a render and initialization function. Its constructor takes a shader object that it uses for all future rendering.
    430 </p>
    431 
    432 <h3>Initialization</h3>
    433 <p>
    434   First, let's delve into the <fun>initRenderData</fun> function that configures the <var>quadVAO</var>:
    435 </p>
    436 
    437 <pre><code>
    438 void SpriteRenderer::initRenderData()
    439 {
    440     // configure VAO/VBO
    441     unsigned int VBO;
    442     float vertices[] = { 
    443         // pos      // tex
    444         0.0f, 1.0f, 0.0f, 1.0f,
    445         1.0f, 0.0f, 1.0f, 0.0f,
    446         0.0f, 0.0f, 0.0f, 0.0f, 
    447     
    448         0.0f, 1.0f, 0.0f, 1.0f,
    449         1.0f, 1.0f, 1.0f, 1.0f,
    450         1.0f, 0.0f, 1.0f, 0.0f
    451     };
    452 
    453     <function id='33'>glGenVertexArrays</function>(1, &this-&gt;quadVAO);
    454     <function id='12'>glGenBuffers</function>(1, &VBO);
    455     
    456     <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, VBO);
    457     <function id='31'>glBufferData</function>(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    458 
    459     <function id='27'>glBindVertexArray</function>(this-&gt;quadVAO);
    460     <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(0);
    461     <function id='30'>glVertexAttribPointer</function>(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
    462     <function id='32'>glBindBuffer</function>(GL_ARRAY_BUFFER, 0);  
    463     <function id='27'>glBindVertexArray</function>(0);
    464 }
    465 </code></pre>
    466 
    467 <p>
    468   Here we first define a set of vertices with (<code>0,0</code>) being the top-left corner of the quad. This means that when we apply translation or scaling transformations on the quad, they're transformed from the top-left position of the quad. This is commonly accepted in 2D graphics and/or GUI systems where elements' positions correspond to the top-left corner of the elements.
    469 </p>
    470 
    471 <p>
    472   Next we simply sent the vertices to the GPU and configure the vertex attributes, which in this case is a single vertex attribute. We only have to define a single VAO for the sprite renderer since all sprites share the same vertex data.
    473 </p>
    474 
    475 <h3>Rendering</h3>
    476 <p>
    477   Rendering sprites is not too difficult; we use the sprite renderer's shader, configure a model matrix, and set the relevant uniforms. What is important here is the order of transformations:
    478 </p>
    479 
    480 <pre><code>
    481 void SpriteRenderer::DrawSprite(Texture2D &texture, glm::vec2 position, 
    482   glm::vec2 size, float rotate, glm::vec3 color)
    483 {
    484     // prepare transformations
    485     this-&gt;shader.Use();
    486     glm::mat4 model = glm::mat4(1.0f);
    487     model = <function id='55'>glm::translate</function>(model, glm::vec3(position, 0.0f));  
    488 
    489     model = <function id='55'>glm::translate</function>(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.0f)); 
    490     model = <function id='57'>glm::rotate</function>(model, <function id='63'>glm::radians</function>(rotate), glm::vec3(0.0f, 0.0f, 1.0f)); 
    491     model = <function id='55'>glm::translate</function>(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
    492 
    493     model = <function id='56'>glm::scale</function>(model, glm::vec3(size, 1.0f)); 
    494   
    495     this-&gt;shader.SetMatrix4("model", model);
    496     this-&gt;shader.SetVector3f("spriteColor", color);
    497   
    498     <function id='49'>glActiveTexture</function>(GL_TEXTURE0);
    499     texture.Bind();
    500 
    501     <function id='27'>glBindVertexArray</function>(this-&gt;quadVAO);
    502     <function id='1'>glDrawArrays</function>(GL_TRIANGLES, 0, 6);
    503     <function id='27'>glBindVertexArray</function>(0);
    504 }  
    505 </code></pre>
    506 
    507 <p>
    508   When trying to position objects somewhere in a scene with rotation and scaling transformations, it is advised to first scale, then rotate, and finally translate the object. Because multiplying matrices occurs from right to left, we transform the matrix in reverse order: translate, rotate, and then scale. 
    509 </p>
    510 
    511 <p>
    512   The rotation transformation may still seem a bit daunting. We know from the <a href="https://learnopengl.com/Getting-started/Transformations" target="_blank">transformations</a> chapter that rotations always revolve around the origin (<code>0,0</code>). Because we specified the quad's vertices with (<code>0,0</code>) as the top-left coordinate, all rotations will rotate around this point of (<code>0,0</code>). The <def>origin of rotation</def> is at the top-left of the quad, which produces undesirable results. What we want to do is move the origin of rotation to the center of the quad so the quad neatly rotates around this origin, instead of rotating around the top-left of the quad. We solve this by translating the quad by half its size first, so its center is at coordinate (<code>0,0</code>) before rotating.
    513 </p>
    514 
    515 <img src="/img/in-practice/breakout/rotation-origin.png" class="clean" alt="Properly rotating at the center of origin of the quad"/>
    516 
    517 <p>
    518   Since we first scale the quad, we have to take the size of the sprite into account when translating to the sprite's center, which is why we multiply with the sprite's <var>size</var> vector. Once the rotation transformation is applied, we reverse the previous translation.
    519 </p>
    520 
    521 <p>
    522   Combining all these transformations, we can position, scale, and rotate each sprite in any way we like. Below you can find the complete source code of the sprite renderer:
    523 </p>
    524 
    525 <ul>
    526   <li><strong>SpriteRenderer</strong>: <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/sprite_renderer.h" target="_blank">header</a>, <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/sprite_renderer.cpp" target="_blank">code</a> </li>
    527 </ul>
    528 
    529 <h2>Hello sprite</h2>
    530 <p>
    531   With the <fun>SpriteRenderer</fun> class we finally have the ability to render actual images to the screen! Let's initialize one within the game code and load our favorite <a href="/img/textures/awesomeface.png" target="_blank">texture</a> while we're at it:
    532 </p>
    533 
    534 <pre><code>
    535 SpriteRenderer  *Renderer;
    536   
    537 void Game::Init()
    538 {
    539     // load shaders
    540     ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
    541     // configure shaders
    542     glm::mat4 projection = <function id='59'>glm::ortho</function>(0.0f, static_cast&lt;float&gt;(this-&gt;Width), 
    543         static_cast&lt;float&gt;(this-&gt;Height), 0.0f, -1.0f, 1.0f);
    544     ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
    545     ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
    546     // set render-specific controls
    547     Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
    548     // load textures
    549     ResourceManager::LoadTexture("textures/awesomeface.png", true, "face");
    550 }
    551 </code></pre>
    552 
    553 <p>
    554   Then within the render function we can render our beloved mascot to see if everything works as it should:
    555 </p>
    556 
    557 <pre><code>
    558 void Game::Render()
    559 {
    560     Renderer-&gt;DrawSprite(ResourceManager::GetTexture("face"), 
    561         glm::vec2(200.0f, 200.0f), glm::vec2(300.0f, 400.0f), 45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
    562 }  
    563 </code></pre>
    564 
    565 <p>
    566   Here we position the sprite somewhat close to the center of the screen with its height being slightly larger than its width. We also rotate it by 45 degrees and give it a green color. Note that the position we give the sprite equals the top-left vertex of the sprite's quad.
    567 </p>
    568 
    569 <p>
    570   If you did everything right you should get the following output:
    571 </p>
    572 
    573 <img src="/img/in-practice/breakout/rendering-sprites.png" class="clean" alt="Image of a rendered sprite using our custom-made OpenGL's SpriteRenderer class"/>
    574 
    575 <p>
    576   You can find the updated game class's source code <a href="/code_viewer_gh.php?code=src/7.in_practice/3.2d_game/0.full_source/progress/3.game.cpp" target="_blank">here</a>.
    577 </p>
    578 
    579 <p>
    580   Now that we got the render systems working, we can put it to good use in the <a href="https://learnopengl.com/In-Practice/2D-Game/Levels" target="_blank">next</a> chapter where  we'll work on building the game's levels.
    581 </p>       
    582 
    583     </div>
    584     
    585     <div id="hover">
    586         HI
    587     </div>
    588    <!-- 728x90/320x50 sticky footer -->
    589 <div id="waldo-tag-6196"></div>
    590 
    591    <div id="disqus_thread"></div>
    592 
    593     
    594 
    595 
    596 </div> <!-- container div -->
    597 
    598 
    599 </div> <!-- super container div -->
    600 </body>
    601 </html>
    602 	</main>
    603 </body>
    604 </html>