<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrei Lesnitsky</title>
    <description>The latest articles on DEV Community by Andrei Lesnitsky (@lesnitsky).</description>
    <link>https://dev.to/lesnitsky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F150381%2Ff6916058-c14c-4bfe-97ce-67e9f7b11d74.jpg</url>
      <title>DEV Community: Andrei Lesnitsky</title>
      <link>https://dev.to/lesnitsky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lesnitsky"/>
    <language>en</language>
    <item>
      <title>How to access Scaffold methods in stateless Flutter widget</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Tue, 19 Nov 2019 17:07:14 +0000</pubDate>
      <link>https://dev.to/lesnitsky/how-to-access-scaffold-methods-in-stateless-flutter-widget-4jfh</link>
      <guid>https://dev.to/lesnitsky/how-to-access-scaffold-methods-in-stateless-flutter-widget-4jfh</guid>
      <description>&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHgVe96R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-tutor-assets.s3.eu-west-2.amazonaws.com/git-tutor-logo-50.png" alt="Git Tutor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/flutter_blog"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B4ZUyJsJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/github/stars/lesnitsky/flutter_blog.svg%3Fstyle%3Dsocial%26hash%3D20191119" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OTSIDKxf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/twitter/follow/lesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3D20191119" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi 👋&lt;/p&gt;

&lt;p&gt;This post will demonstrate how to access &lt;code&gt;ScaffoldState&lt;/code&gt; methods (like &lt;code&gt;showSnackBar&lt;/code&gt;) in &lt;code&gt;StatelessWidget&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's reproduce a simple example demonstrating the issue and create a &lt;code&gt;HomePage&lt;/code&gt; stateless widget with a &lt;code&gt;Scaffold&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 lib/main.dart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;      );
    }
  }
&lt;span class="gi"&gt;+ 
+ class HomePage extends StatelessWidget {
+   @override
+   Widget build(BuildContext context) {
+     return Scaffold(
+       appBar: AppBar(
+         title: Text('Stateless Widget Scaffold'),
+       ),
+     );
+   }
+ }
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add a button which will call &lt;code&gt;_showSnackbar&lt;/code&gt; when pressed&lt;/p&gt;

&lt;p&gt;📄 lib/main.dart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;        appBar: AppBar(
          title: Text('Stateless Widget Scaffold'),
        ),
&lt;span class="gi"&gt;+       body: Center(
+         child: FlatButton(
+           child: Text("Show snackbar"),
+           onPressed: _showSnackbar,
+         ),
+       ),
&lt;/span&gt;      );
    }
  }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can use &lt;code&gt;GlobalKey&lt;/code&gt; to access &lt;code&gt;ScaffoldState&lt;/code&gt;, so let's create one&lt;/p&gt;

&lt;p&gt;📄 lib/main.dart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;  }

  class HomePage extends StatelessWidget {
&lt;span class="gi"&gt;+   final scaffoldKey = new GlobalKey();
+ 
&lt;/span&gt;    @override
    Widget build(BuildContext context) {
      return Scaffold(

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;pass it to &lt;code&gt;Scaffold&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 lib/main.dart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;    @override
    Widget build(BuildContext context) {
      return Scaffold(
&lt;span class="gi"&gt;+       key: scaffoldKey,
&lt;/span&gt;        appBar: AppBar(
          title: Text('Stateless Widget Scaffold'),
        ),

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and implement method &lt;code&gt;_showSnackbar&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 lib/main.dart&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;  class HomePage extends StatelessWidget {
    final scaffoldKey = new GlobalKey();

+   _showSnackbar() {
&lt;span class="gi"&gt;+     (scaffoldKey.currentState as ScaffoldState).showSnackBar(
+       SnackBar(
+         content: Text("I'm snackbar!"),
+       ),
+     );
+   }
+ 
&lt;/span&gt;    @override
    Widget build(BuildContext context) {
      return Scaffold(

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's it! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/flutter_blog/tree/blog/stateless_widget_scaffold"&gt;Soruce code is available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dbX_CuTT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://screenshots-lesnitsky.s3.eu-west-2.amazonaws.com/stateless-scaffold-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dbX_CuTT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://screenshots-lesnitsky.s3.eu-west-2.amazonaws.com/stateless-scaffold-screenshot.png" alt="Stateless widget scaffold screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHgVe96R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-tutor-assets.s3.eu-west-2.amazonaws.com/git-tutor-logo-50.png" alt="Git Tutor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/flutter_blog"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B4ZUyJsJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/github/stars/lesnitsky/flutter_blog.svg%3Fstyle%3Dsocial%26hash%3D20191119" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OTSIDKxf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/twitter/follow/lesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3D20191119" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>beginners</category>
    </item>
    <item>
      <title>WebGL Month. Day 31. WebGL Month summary</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Wed, 31 Jul 2019 15:11:21 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-31-webgl-month-summary-4pd4</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-31-webgl-month-summary-4pd4</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pFsCH1YQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/github/stars/lesnitsky/webgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday31" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MgQC6ep8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/twitter/follow/lesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday31" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHgVe96R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-tutor-assets.s3.eu-west-2.amazonaws.com/git-tutor-logo-50.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to the last day of WebGL month.&lt;br&gt;
This article won't cover any new topics, but rather summarize previous 30 days&lt;/p&gt;

&lt;h3&gt;
  
  
  Previuos tutorials:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-1-19ha"&gt;Day 1. Intro&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This article doesn't cover any WebGL topics, but rather explains what WebGL does under the hood. TL;DR: it calculates colors of each pixel it has to draw&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/shaders-and-points-3h2c"&gt;Day 2. Shaders and points&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Introduction to WebGL API and GLSL shaders with the simpliest possible primitive type – point&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-3-shader-uniforms-lines-and-triangles-5dof"&gt;Day 3. Shader uniforms, lines and triangles&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This article covers more ways of passing data to shaders and uses more complex primitives to render&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/shader-varyings-2p0f"&gt;Day 4. Shader varying&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Passing data from vertex to fragment shader with varyings&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-5-interleaved-buffers-2k9a"&gt;Day 5. Interleaved buffers&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Alternative ways of storing and passing vertex data to shaders&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-6-indexed-buffer-ll6"&gt;Day 6. Indexed buffer&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;A technique which helps reduce number of duplicate vertices&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-7-a-bit-of-cleanup-and-tooling-bd4"&gt;Day 7. Cleanup and tooling&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;WebGL is fun, but it requires a bit of tooling when your project grows. Luckily we have awesome tools like webpack&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-8-textures-1mk8"&gt;Day 8. Textures&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Intro to textures&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-9-image-filters-5g8e"&gt;Day 9. Image filters&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Taking advantage of fragment shader to implement simple image "filters" (inverse, black and white, sepia)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-10-multiple-textures-gf3"&gt;Day 10. Multiple textures&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;How to use multiple textures in a single webgl program&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-11-3plb"&gt;Day 11. Reducing WebGL boilerplate&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Implementation of some utility classes and functions to reduce WebGL boilerplate&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-12-highdpi-displays-and-webgl-viewport-2cg3"&gt;Day 12. Highdpi displays and WebGL viewport&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;How to handle retina displays with canvas and use webgl viewport&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-simple-animation-5hc3"&gt;Day 13. Simple animation&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;All previous examples where static images, this article will add some motion to the scene&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-14-intro-to-3d-2ni2"&gt;Day 14. Intro to 3D&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Theory of 3D compuatations required for 3D rendering. No code&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-15-rendering-a-3d-cube-190f"&gt;Day 15. Rendering a cube&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;3D theory applied on practice to render 3D cube&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-16-colorizing-cube-depth-buffer-and-array-uniforms-4nhc"&gt;Day 16. Depth buffer. Cube faces colors&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This article contains fixes for previous example and adds more colors&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-17-exploring-obj-format-6fn"&gt;Day 17. OBJ format&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Implementing simple parser for OBJ format&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-18-flat-shading-3nhg"&gt;Day 18. Flat shading&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Implementation of flat shading&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-19-rendering-multiple-objects-45m7"&gt;Day 19. Rendering multiple objects&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;A typical 3D scene consists of multiple objects, this tutorial will teach you how to render more than 1 object&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-20-rendering-a-minecraft-dirt-cube-5ag3"&gt;Day 20. Rendering a minecraft dirt cube&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Texturing 3D object with Blender and WebGL&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-21-rendering-a-minecraft-terrain-24b5"&gt;Day 21. Rendering a minecraft terrain&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We've learned how to render multiple objects. How to render 10000 of objects?&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-22-reducing-number-of-webgl-calls-by-5000-times-3a4j"&gt;Day 22. Reducing number of webgl calls by 5000 times&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Previous example worked, but wasn't really performance. This article explains &lt;em&gt;instancing&lt;/em&gt; (a technique which helps to improve performance when rendering a large amount of same objects)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-23-skybox-in-webgl-1eig"&gt;Day 23. Skynox&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Adding "environment" to the scene&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-24-combining-terrain-and-skybox-kgo"&gt;Day 24. Combining terrain and skybox&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;How to use multiple WebGL programs together&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-25-mipmaps-33i"&gt;Day 25. Mipmaps&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;A technique which improves performance of shaders reading data from textures&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-26-rendering-to-texture-4hkp"&gt;Day 26. Rendering to texture&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Rendering to texture allows to apply some "post-effects" and might be used for a variety of use-cases&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-27-click-detection-part-i-5920"&gt;Day 27. Click detection. Part I&lt;/a&gt;
&lt;/h4&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-28-click-detection-part-ii-367e"&gt;Day 28. Click detection. Part II&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Detecting object under the cursor might seem a tough task, but it might be done without complex 3d math in JS&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-29-fog-58od"&gt;Day 29. Fog&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Improving scene with fog&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://dev.to/lesnitsky/webgl-month-day-30-text-rendering-in-webgl-3ih3"&gt;Day 30. Text rendering in WebGL&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;An overview of text rendering techniques in WebGL&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful links
&lt;/h3&gt;

&lt;p&gt;I've started working with WebGL only a year and a half ago. My WebGL journey started with an awesome resource – &lt;a href="https://webglfundamentals.org/"&gt;https://webglfundamentals.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One more important thing to understand: WebGL is just a wrapper of OpenGL, so almost everything from OpenGL tutorials might be used in WebGL as well: &lt;a href="https://learnopengl.com/"&gt;https://learnopengl.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exploring more glsl stuff: &lt;a href="https://thebookofshaders.com/"&gt;https://thebookofshaders.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codepen for shaders: &lt;a href="https://www.shadertoy.com/"&gt;https://www.shadertoy.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL"&gt;Getting started with WebGL tutorial on MDN&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Thanks!
&lt;/h3&gt;

&lt;p&gt;Thanks for joining WebGL month. Hope this articles helped you learn WebGL! 😉&lt;br&gt;
Feel free to submit questions, suggestions, improvements to &lt;a href="https://github.com/lesnitsky/webgl-month"&gt;github repo&lt;/a&gt;, get in touch with me &lt;a href="//mailto:andrei.lesnitsky@gmail.com"&gt;via email&lt;/a&gt; or &lt;a href="https://twitter.com/lesnitsky_a"&gt;twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 30. Text rendering in WebGL</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Tue, 30 Jul 2019 18:56:18 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-30-text-rendering-in-webgl-3ih3</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-30-text-rendering-in-webgl-3ih3</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TUUMAMe4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/github/stars/lesnitsky/webgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday30" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jPnzs8DL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/twitter/follow/lesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsociall%26hash%3Dday30" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;Soruce code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHgVe96R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-tutor-assets.s3.eu-west-2.amazonaws.com/git-tutor-logo-50.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month.&lt;/p&gt;

&lt;p&gt;In previuos tutorials we were focused on rendering 2d and 3d shapes, but never rendered text, which is important part of any application.&lt;/p&gt;

&lt;p&gt;In this article we'll review possible ways of text rendering.&lt;/p&gt;
&lt;h3&gt;
  
  
  HTML overlay
&lt;/h3&gt;

&lt;p&gt;The most obvoius and simple solution would be to render text with HTML and place it above the webgl canvas, but this will only work for 2D scenes, 3D stuff will require some calculations to calculate text position and css transforms&lt;/p&gt;
&lt;h3&gt;
  
  
  Canvas as texture
&lt;/h3&gt;

&lt;p&gt;Other technique might be applied in a wider range of cases. It requires several steps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create another canvas&lt;/li&gt;
&lt;li&gt;get 2d context (&lt;code&gt;canvas.getContext('2d')&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;render text with &lt;code&gt;fillText&lt;/code&gt; or &lt;code&gt;strokeText&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use this canvas as webgl texture with correct texture coordinates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the texture is a rasterized image, it will loose the quality when you'll come "closer" to the object&lt;/p&gt;
&lt;h3&gt;
  
  
  Glyphs texture
&lt;/h3&gt;

&lt;p&gt;Each font is actually a set of "glyphs" – each symbol is rendered in a signle image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A | B | C | D | E | F | G |
---------------------------
H | I | J | K | L | M | N |
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Each letter will have it's own "properties", like width (&lt;code&gt;i&lt;/code&gt; is thiner than &lt;code&gt;W&lt;/code&gt;), height (&lt;code&gt;o&lt;/code&gt; vs &lt;code&gt;L&lt;/code&gt;) etc.&lt;br&gt;
These properties will affect how to build rectangles, containing each letter&lt;/p&gt;

&lt;p&gt;Typically aside of texture you'll need to have a javascript object describing all these properties and coordinates in original texture image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;textureSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;glyphs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and to render some text you'll need something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getRects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizeMultiplier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prevLetterX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;glyph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glyphs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prevLetterX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;glyph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;glyph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sizeMultiplier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;glyph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sizeMultiplier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;glyph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Later this "rects" will be used to generate attributes data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./gl-helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateBuffers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;attributeBuffers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;rects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;attributeBuffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;createRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="nx"&gt;attributeBuffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;createRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;attributeBuffers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There's a &lt;a href="https://www.npmjs.com/package/gl-render-text"&gt;gl-render-text&lt;/a&gt; package which can render texture based fonts&lt;/p&gt;

&lt;h3&gt;
  
  
  Font triangulation
&lt;/h3&gt;

&lt;p&gt;Since webgl is capable of drawing triangles, one more obvious solution would be to break each letter into triangles&lt;br&gt;
This seem to be a very complex task 😢&lt;/p&gt;

&lt;p&gt;Luckily – there's a &lt;a href="https://github.com/mattdesl/fontpath-gl"&gt;fontpath-gl&lt;/a&gt; package, which does exactly this&lt;/p&gt;

&lt;h3&gt;
  
  
  Signed distance field font
&lt;/h3&gt;

&lt;p&gt;Another technique for rendering text in OpenGL/WebGL&lt;/p&gt;

&lt;p&gt;Find &lt;a href="https://github.com/libgdx/libgdx/wiki/Distance-field-fonts"&gt;more info here&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2s3pDsi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/github/stars/lesnitsky/webgl-month.svg%3Fstyle%3Dsociall%26hash%3Dday30" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jPnzs8DL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://img.shields.io/twitter/follow/lesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsociall%26hash%3Dday30" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month"&gt;Soruce code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHgVe96R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://git-tutor-assets.s3.eu-west-2.amazonaws.com/git-tutor-logo-50.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 29. Fog</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Mon, 29 Jul 2019 14:38:02 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-29-fog-58od</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-29-fog-58od</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday29" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday29" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month&lt;/p&gt;

&lt;p&gt;Today we're going to improve our 3D minecraft terrain scene with fog&lt;/p&gt;

&lt;p&gt;Basically we need to "lighten" the color of far cubes (calculate distance between camera and cube vertex)&lt;/p&gt;

&lt;p&gt;To calculate relative distance between camera position and some point, we need to multiply position by view and model matrices. Since we also need the same resulting matrix together with projection matrix, let's just extract it to a variable&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
&lt;span class="gd"&gt;-     gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
&lt;/span&gt;&lt;span class="gi"&gt;+     mat4 modelView = viewMatrix * modelMatrix;
+ 
+     gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      vTexCoord = texCoord;
      vColor = encodeObject(index);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our camera looks in a negative direction of Z axis, we need to get &lt;code&gt;z&lt;/code&gt; coordinate of resulting vertex position&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;      gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     float depth = (modelView * vec4(position, 1.0)).z;
+ 
&lt;/span&gt;      vTexCoord = texCoord;
      vColor = encodeObject(index);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this value will be negative, while we need a positive value, so let's just negate it&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;      gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     float depth = (modelView * vec4(position, 1.0)).z;
&lt;/span&gt;&lt;span class="gi"&gt;+     float depth = -(modelView * vec4(position, 1.0)).z;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      vTexCoord = texCoord;
      vColor = encodeObject(index);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can't use &lt;code&gt;depth&lt;/code&gt; directly, since we need a value in &lt;code&gt;[0..1]&lt;/code&gt; range. Also it'd be nice to have a smooth "gradient" like fog. We can apply glsl &lt;a href="https://thebookofshaders.com/glossary/?search=smoothstep" rel="noopener noreferrer"&gt;smoothstep&lt;/a&gt; function to calcuate the final amount of fog. This function interpolates a value in range of &lt;code&gt;lowerBound&lt;/code&gt; and &lt;code&gt;upperBound&lt;/code&gt;. Max depth of our camera is &lt;code&gt;142&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;142&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- zFar&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the max value of &lt;code&gt;depth&lt;/code&gt; should be &amp;lt; 142 in order to see any fog at all (object farther than 142 won't be visible at all). Let's use &lt;code&gt;60..100&lt;/code&gt; range.&lt;/p&gt;

&lt;p&gt;One more thing to take into account is that we don't want to see the object &lt;em&gt;completely&lt;/em&gt; white, so let's multiply the final amount by &lt;code&gt;0.9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We'll need the final value of &lt;code&gt;fogAmount&lt;/code&gt; in fragment shader, so this should be a &lt;code&gt;varying&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  varying vec2 vTexCoord;
  varying vec3 vColor;
  varying vec4 vColorMultiplier;
&lt;span class="gi"&gt;+ varying float vFogAmount;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  vec3 encodeObject(float id) {
      int b = int(mod(id, 255.0));
      gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
&lt;span class="err"&gt;
&lt;/span&gt;      float depth = -(modelView * vec4(position, 1.0)).z;
&lt;span class="gi"&gt;+     vFogAmount = smoothstep(60.0, 100.0, depth) * 0.9;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      vTexCoord = texCoord;
      vColor = encodeObject(index);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define this varying in fragment shader&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  uniform float renderIndices;
  varying vec4 vColorMultiplier;
&lt;span class="gi"&gt;+ varying float vFogAmount;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
      gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's define a color of the fog (white). We can also pass this color to a uniform, but let's keep things simple&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  void main() {
      gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     vec3 fogColor = vec3(1.0, 1.0, 1.0);
+ 
&lt;/span&gt;      if (renderIndices == 1.0) {
          gl_FragColor.rgb = vColor;
      }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and finally we need to mix original color of the pixel with the fog. We can use glsl &lt;a href="https://thebookofshaders.com/glossary/?search=mix" rel="noopener noreferrer"&gt;mix&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
&lt;span class="err"&gt;
&lt;/span&gt;      vec3 fogColor = vec3(1.0, 1.0, 1.0);
&lt;span class="gi"&gt;+     gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, vFogAmount);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      if (renderIndices == 1.0) {
          gl_FragColor.rgb = vColor;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, our scene is now "foggy". To implement the same effect, but "at night", we just need to change fog color to black.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday29" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday29" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 28. Click detection. Part II</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Sun, 28 Jul 2019 18:21:30 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-28-click-detection-part-ii-367e</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-28-click-detection-part-ii-367e</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday28" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday28" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month&lt;/p&gt;

&lt;p&gt;Yesterday we've rendered our minecraft terrain to a offscreen texture, where each object is encoded into a specific color and learned how to read pixel colors from the texture back to JS. Now let's decode this color to an object index and highlight selected cube&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gl.readPixels&lt;/code&gt; fills the &lt;code&gt;Uint8Array&lt;/code&gt; with pixel colors startig from the bottom left corner. We need to convert client coordinates to the pixels coordinate in the array. Don't forget the pixel ration, since our offscreen framebuffer takes it into account, and event coordinates don't.&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- document.body.addEventListener('click', () =&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+ document.body.addEventListener('click', (e) =&amp;gt; {
&lt;/span&gt;      coloredCubesRenderBuffer.bind(gl);
&lt;span class="err"&gt;
&lt;/span&gt;      renderTerrain(gl, viewMatrix, projectionMatrix, true);
&lt;span class="err"&gt;
&lt;/span&gt;      const pixels = new Uint8Array(canvas.width * canvas.height * 4);
      gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
&lt;span class="gi"&gt;+ 
+     const x = e.clientX * devicePixelRatio;
+     const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to skip &lt;code&gt;y&lt;/code&gt; rows (&lt;code&gt;y * canvas.width&lt;/code&gt;) multiplied by 4 (4 integers per pixel)&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;      const x = e.clientX * devicePixelRatio;
      const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
&lt;span class="gi"&gt;+ 
+     const rowsToSkip = y * canvas.width * 4;
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Horizontal coordinate is &lt;code&gt;x * 4&lt;/code&gt; (coordinate multiplied by number of integers per pixel)&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      const y = (canvas.offsetHeight - e.clientY) * devicePixelRatio;
&lt;span class="err"&gt;
&lt;/span&gt;      const rowsToSkip = y * canvas.width * 4;
&lt;span class="gi"&gt;+     const col = x * 4;
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the final index of pixel is rowsToSkip + col&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;      const rowsToSkip = y * canvas.width * 4;
      const col = x * 4;
&lt;span class="gi"&gt;+ 
+     const pixelIndex = rowsToSkip + col;
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to read each pixel color component&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      const col = x * 4;
&lt;span class="err"&gt;
&lt;/span&gt;      const pixelIndex = rowsToSkip + col;
&lt;span class="gi"&gt;+ 
+     const r = pixels[pixelIndex];
+     const g = pixels[pixelIndex + 1];
+     const b = pixels[pixelIndex + 2];
+     const a = pixels[pixelIndex + 3];
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to convert back to integer from r g b&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ function rgbToInt(r, g, b) {
+     return b + g * 255 + r * 255 ** 2;
+ }
+ 
&lt;/span&gt;  document.body.addEventListener('click', (e) =&amp;gt; {
      coloredCubesRenderBuffer.bind(gl);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's drop camera rotation code to make scene static&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  function render() {
      offscreenRenderBuffer.clear(gl);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
-     mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
-     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, 30]);
- 
-     mat4.getTranslation(cameraFocusPoint, cameraFocusPointMatrix);
- 
&lt;/span&gt;      mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
&lt;span class="err"&gt;
&lt;/span&gt;      renderSkybox(gl, viewMatrix, projectionMatrix);
      const g = pixels[pixelIndex + 1];
      const b = pixels[pixelIndex + 2];
      const a = pixels[pixelIndex + 3];
&lt;span class="gi"&gt;+ 
+     const index = rgbToInt(r, g, b);
+ 
+     console.log(index);
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and update initial camera position to see the scene better&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const cameraPosition = [0, 5, 0];
- const cameraFocusPoint = vec3.fromValues(0, 0, 30);
&lt;/span&gt;&lt;span class="gi"&gt;+ const cameraPosition = [0, 10, 0];
+ const cameraFocusPoint = vec3.fromValues(30, 0, 30);
&lt;/span&gt;  const cameraFocusPointMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;  mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next let's pass selected color index into vertex shader as varying&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  uniform mat4 viewMatrix;
  uniform mat4 projectionMatrix;
&lt;span class="gi"&gt;+ uniform float selectedObjectIndex;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  varying vec2 vTexCoord;
  varying vec3 vColor;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And multiply object color if its index matches selected object index&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  varying vec3 vColor;
&lt;span class="err"&gt;
&lt;/span&gt;  uniform float renderIndices;
&lt;span class="gi"&gt;+ varying vec4 vColorMultiplier;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
&lt;span class="gd"&gt;-     gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1));
&lt;/span&gt;&lt;span class="gi"&gt;+     gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      if (renderIndices == 1.0) {
          gl_FragColor.rgb = vColor;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  varying vec2 vTexCoord;
  varying vec3 vColor;
&lt;span class="gi"&gt;+ varying vec4 vColorMultiplier;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  vec3 encodeObject(float id) {
      int b = int(mod(id, 255.0));
&lt;span class="err"&gt;
&lt;/span&gt;      vTexCoord = texCoord;
      vColor = encodeObject(index);
&lt;span class="gi"&gt;+     
+     if (selectedObjectIndex == index) {
+         vColorMultiplier = vec4(1.5, 1.5, 1.5, 1.0);
+     } else {
+         vColorMultiplier = vec4(1.0, 1.0, 1.0, 1.0);
+     }
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and reflect shader changes in js&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.index, 0);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- export function render(gl, viewMatrix, projectionMatrix, renderIndices) {
&lt;/span&gt;&lt;span class="gi"&gt;+ export function render(gl, viewMatrix, projectionMatrix, renderIndices, selectedObjectIndex) {
&lt;/span&gt;      gl.useProgram(State.program);
&lt;span class="err"&gt;
&lt;/span&gt;      setupAttributes(gl);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.viewMatrix, false, viewMatrix);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.uniform1f(State.programInfo.uniformLocations.selectedObjectIndex, selectedObjectIndex);
+ 
&lt;/span&gt;      if (renderIndices) {
          gl.uniform1f(State.programInfo.uniformLocations.renderIndices, 1);
      } else {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ let selectedObjectIndex = -1;
+ 
&lt;/span&gt;  function render() {
      offscreenRenderBuffer.clear(gl);
&lt;span class="err"&gt;
&lt;/span&gt;      mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
&lt;span class="err"&gt;
&lt;/span&gt;      renderSkybox(gl, viewMatrix, projectionMatrix);
&lt;span class="gd"&gt;-     renderTerrain(gl, viewMatrix, projectionMatrix);
&lt;/span&gt;&lt;span class="gi"&gt;+     renderTerrain(gl, viewMatrix, projectionMatrix, false, selectedObjectIndex);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      gl.useProgram(program);
&lt;span class="err"&gt;

&lt;/span&gt;      const index = rgbToInt(r, g, b);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     console.log(index);
&lt;/span&gt;&lt;span class="gi"&gt;+     selectedObjectIndex = index;
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! We now know selected object index, so that we can perform JS operations as well as visual feedback!&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday28" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday28" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 27. Click detection. Part I</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Sat, 27 Jul 2019 14:22:17 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-27-click-detection-part-i-5920</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-27-click-detection-part-i-5920</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday27" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday27" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Soruce code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Yesterday we've learned how to render to a texture. This is a nice ability to make some nice effects after the scene was completely rendered, but we can get advantage of offscreen rendering for something else.&lt;/p&gt;

&lt;p&gt;One important thing in interactive 3D is click detection. While it may be done with javascript, it involves some complex math. Instead we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  assign a unique solid color to each object&lt;/li&gt;
&lt;li&gt;  render scene to a texture&lt;/li&gt;
&lt;li&gt;  read pixel color under cursor&lt;/li&gt;
&lt;li&gt;  match color with an object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we'll need another framebuffer, let's create a helper class&lt;/p&gt;

&lt;p&gt;📄 src/RenderBuffer.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderBuffer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;framebuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFramebuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTexture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup framebuffer and color texture&lt;/p&gt;

&lt;p&gt;📄 src/RenderBuffer.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      constructor(gl) {
          this.framebuffer = gl.createFramebuffer();
          this.texture = gl.createTexture();
&lt;span class="gi"&gt;+ 
+         gl.bindTexture(gl.TEXTURE_2D, this.texture);
+         gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.canvas.width, gl.canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ 
+         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ 
+         gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+         gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup depth buffer&lt;/p&gt;

&lt;p&gt;📄 src/RenderBuffer.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;          gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
          gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
&lt;span class="gi"&gt;+ 
+         this.depthBuffer = gl.createRenderbuffer();
+         gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer);
+ 
+         gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, gl.canvas.width, gl.canvas.height);
+         gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer);
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement bind method&lt;/p&gt;

&lt;p&gt;📄 src/RenderBuffer.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;          gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, gl.canvas.width, gl.canvas.height);
          gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer);
      }
&lt;span class="gi"&gt;+ 
+     bind(gl) {
+         gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
+     }
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and clear&lt;/p&gt;

&lt;p&gt;📄 src/RenderBuffer.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      bind(gl) {
          gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
      }
&lt;span class="gi"&gt;+ 
+     clear(gl) {
+         this.bind(gl);
+         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+     }
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use new helper class&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  import { setupShaderInput, compileShader } from './gl-helpers';
  import { GLBuffer } from './GLBuffer';
  import { createRect } from './shape-helpers';
&lt;span class="gi"&gt;+ import { RenderBuffer } from './RenderBuffer';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
&lt;span class="err"&gt;
&lt;/span&gt;  mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const framebuffer = gl.createFramebuffer();
- 
- const texture = gl.createTexture();
- 
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- 
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- 
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
- 
- const depthBuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
- 
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, canvas.width, canvas.height);
- gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
&lt;/span&gt;&lt;span class="gi"&gt;+ const offscreenRenderBuffer = new RenderBuffer(gl);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const vShader = gl.createShader(gl.VERTEX_SHADER);
  const fShader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;  function render() {
&lt;span class="gd"&gt;-     gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
- 
-     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
&lt;/span&gt;&lt;span class="gi"&gt;+     offscreenRenderBuffer.clear(gl);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
      gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
&lt;span class="gd"&gt;-     gl.bindTexture(gl.TEXTURE_2D, texture);
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.bindTexture(gl.TEXTURE_2D, offscreenRenderBuffer.texture);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of passing the whole unique color of the object, which is a vec3, we can pass only object index&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  attribute vec3 position;
  attribute vec2 texCoord;
  attribute mat4 modelMatrix;
&lt;span class="gi"&gt;+ attribute float index;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  uniform mat4 viewMatrix;
  uniform mat4 projectionMatrix;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and convert this float to a color right in the shader&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  varying vec2 vTexCoord;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ vec3 encodeObject(float id) {
+     int b = int(mod(id, 255.0));
+     int r = int(id) / 255 / 255;
+     int g = (int(id) - b - r * 255 * 255) / 255;
+     return vec3(r, g, b) / 255.0;
+ }
+ 
&lt;/span&gt;  void main() {
      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to pass the color to a fragment shader via varying&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  uniform sampler2D texture;
&lt;span class="err"&gt;
&lt;/span&gt;  varying vec2 vTexCoord;
&lt;span class="gi"&gt;+ varying vec3 vColor;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
      gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1));
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  uniform mat4 projectionMatrix;
&lt;span class="err"&gt;
&lt;/span&gt;  varying vec2 vTexCoord;
&lt;span class="gi"&gt;+ varying vec3 vColor;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  vec3 encodeObject(float id) {
      int b = int(mod(id, 255.0));
      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
&lt;span class="err"&gt;
&lt;/span&gt;      vTexCoord = texCoord;
&lt;span class="gi"&gt;+     vColor = encodeObject(index);
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to specify what do we want to render: textured object or colored, so let's use a uniform for it&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.f.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  varying vec2 vTexCoord;
  varying vec3 vColor;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ uniform float renderIndices;
+ 
&lt;/span&gt;  void main() {
      gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1));
&lt;span class="gi"&gt;+ 
+     if (renderIndices == 1.0) {
+         gl_FragColor.rgb = vColor;
+     }
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create indices array&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      State.modelMatrix = mat4.create();
      State.rotationMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     const indices = new Float32Array(100 * 100);
+ 
&lt;/span&gt;      let cubeIndex = 0;
&lt;span class="err"&gt;
&lt;/span&gt;      for (let i = -50; i &amp;lt; 50; i++) {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fill it with data and setup a GLBuffer&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;                  matrices[cubeIndex * 4 * 4 + index] = value;
              });
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+             indices[cubeIndex] = cubeIndex;
+ 
&lt;/span&gt;              cubeIndex++;
          }
      }
&lt;span class="err"&gt;
&lt;/span&gt;      State.matricesBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, matrices, gl.STATIC_DRAW);
&lt;span class="gi"&gt;+     State.indexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, indices, gl.STATIC_DRAW);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      State.offset = 4 * 4; // 4 floats 4 bytes each
      State.stride = State.offset * 4; // 4 rows of 4 floats
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we have a new attribute, we need to update setupAttribute and resetDivisorAngles functions&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;          State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.modelMatrix + i, 1);
      }
&lt;span class="gi"&gt;+ 
+     State.indexBuffer.bind(gl);
+     gl.vertexAttribPointer(State.programInfo.attributeLocations.index, 1, gl.FLOAT, false, 0, 0);
+     State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.index, 1);
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  function resetDivisorAngles() {
      for (let i = 0; i &amp;lt; 4; i++) {
          State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.modelMatrix + i, 0);
      }
&lt;span class="gi"&gt;+ 
+     State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.index, 0);
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  export function render(gl, viewMatrix, projectionMatrix) {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally we need another argument of a render function to distinguish between "render modes" (either textured cubes or colored)&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      State.ext.vertexAttribDivisorANGLE(State.programInfo.attributeLocations.index, 0);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- export function render(gl, viewMatrix, projectionMatrix) {
&lt;/span&gt;&lt;span class="gi"&gt;+ export function render(gl, viewMatrix, projectionMatrix, renderIndices) {
&lt;/span&gt;      gl.useProgram(State.program);
&lt;span class="err"&gt;
&lt;/span&gt;      setupAttributes(gl);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.viewMatrix, false, viewMatrix);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     if (renderIndices) {
+         gl.uniform1f(State.programInfo.uniformLocations.renderIndices, 1);
+     } else {
+         gl.uniform1f(State.programInfo.uniformLocations.renderIndices, 0);
+     }
+ 
&lt;/span&gt;      State.ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, State.vertexBuffer.data.length / 3, 100 * 100);
&lt;span class="err"&gt;
&lt;/span&gt;      resetDivisorAngles();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need another render buffer to render colored cubes to&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
&lt;span class="err"&gt;
&lt;/span&gt;  const offscreenRenderBuffer = new RenderBuffer(gl);
&lt;span class="gi"&gt;+ const coloredCubesRenderBuffer = new RenderBuffer(gl);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const vShader = gl.createShader(gl.VERTEX_SHADER);
  const fShader = gl.createShader(gl.FRAGMENT_SHADER);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's add a click listeneer&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ document.body.addEventListener('click', () =&amp;gt; {
+     coloredCubesRenderBuffer.bind(gl);
+ });
+ 
&lt;/span&gt;  (async () =&amp;gt; {
      await prepareSkybox(gl);
      await prepareTerrain(gl);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and render colored cubes to a texture each time user clicks on a canvas&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  document.body.addEventListener('click', () =&amp;gt; {
      coloredCubesRenderBuffer.bind(gl);
&lt;span class="gi"&gt;+ 
+     renderTerrain(gl, viewMatrix, projectionMatrix, true);
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need a storage to read pixel colors to&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      coloredCubesRenderBuffer.bind(gl);
&lt;span class="err"&gt;
&lt;/span&gt;      renderTerrain(gl, viewMatrix, projectionMatrix, true);
&lt;span class="gi"&gt;+ 
+     const pixels = new Uint8Array(canvas.width * canvas.height * 4);
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and actually read pixel colors&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      renderTerrain(gl, viewMatrix, projectionMatrix, true);
&lt;span class="err"&gt;
&lt;/span&gt;      const pixels = new Uint8Array(canvas.width * canvas.height * 4);
&lt;span class="gi"&gt;+     gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
&lt;/span&gt;  });
&lt;span class="err"&gt;
&lt;/span&gt;  (async () =&amp;gt; {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, we now have the whole scene rendered to an offscreen texture, where each object has a unique color. We'll continue click detection tomorrow&lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday27" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday27" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Soruce code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 26. Rendering to texture</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Fri, 26 Jul 2019 16:14:40 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-26-rendering-to-texture-4hkp</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-26-rendering-to-texture-4hkp</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday26" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday26" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Hey 👋 Welcome to WebGL month.&lt;/p&gt;

&lt;p&gt;In one of our previous tutorials we've build some simple image filters, like "black and white", "sepia", etc.&lt;br&gt;
Can we apply this "post effects" not only to an existing image, but to the whole 3d scene we're rendering?&lt;/p&gt;

&lt;p&gt;Yes, we can! However we'll still need a texture to process, so we need to render our scene not to a canvas, but to a texture first&lt;/p&gt;

&lt;p&gt;As we know from the very first tutorial, canvas is just a buffer of pixel colors (4 integers, r, g, b, a)&lt;br&gt;
There's also a depth buffer (for Z coordinate of each pixel)&lt;/p&gt;

&lt;p&gt;So the idea is to make webgl render to some different "buffer" instead of canvas.&lt;/p&gt;

&lt;p&gt;There's a special type of buffer, called &lt;code&gt;framebuffer&lt;/code&gt; which can be treated as a render target&lt;/p&gt;

&lt;p&gt;To create a framebuffer we need to call &lt;code&gt;gl.createFramebuffer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;  mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const framebuffer = gl.createFramebuffer();
+ 
&lt;/span&gt;  function render() {
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Framebuffer itself is not a storage, but rather a set of references to "attachments" (color, depth)&lt;/p&gt;

&lt;p&gt;To render colors we'll need a texture&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;  const framebuffer = gl.createFramebuffer();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const texture = gl.createTexture();
+ 
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ 
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ 
&lt;/span&gt;  function render() {
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we need to bind a framebuffer and setup a color attachment&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ 
&lt;/span&gt;  function render() {
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now our canvas is white. Did we break something? No – everything is fine, but our scene is now rendered to a texture instead of canvas&lt;/p&gt;

&lt;p&gt;Now we need to render from texture to canvas&lt;/p&gt;

&lt;p&gt;Vertex shader is very simple, we just need to render a canvas-size rectangle, so we can pass vertex positions from js without any transformations&lt;/p&gt;

&lt;p&gt;📄 src/shaders/filter.v.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;

&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Fragment shader needs a texture to read a color from and resolution to transform pixel coordinates to texture coordinates&lt;/p&gt;

&lt;p&gt;📄 src/shaders/filter.f.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;

&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;gl_FragCoord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we need to go through a program setup routine&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  import { prepare as prepareSkybox, render as renderSkybox } from './skybox';
  import { prepare as prepareTerrain, render as renderTerrain } from './minecraft-terrain';
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ import vShaderSource from './shaders/filter.v.glsl';
+ import fShaderSource from './shaders/filter.f.glsl';
+ import { setupShaderInput, compileShader } from './gl-helpers';
+ import { GLBuffer } from './GLBuffer';
+ import { createRect } from './shape-helpers';
+ 
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
&lt;span class="err"&gt;
&lt;/span&gt;  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const vShader = gl.createShader(gl.VERTEX_SHADER);
+ const fShader = gl.createShader(gl.FRAGMENT_SHADER);
+ 
+ compileShader(gl, vShader, vShaderSource);
+ compileShader(gl, fShader, fShaderSource);
+ 
+ const program = gl.createProgram();
+ 
+ gl.attachShader(program, vShader);
+ gl.attachShader(program, fShader);
+ 
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ 
+ const vertexPositionBuffer = new GLBuffer(
+     gl,
+     gl.ARRAY_BUFFER,
+     new Float32Array([...createRect(-1, -1, 2, 2)]),
+     gl.STATIC_DRAW
+ );
+ 
+ const indexBuffer = new GLBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 1, 2, 3]), gl.STATIC_DRAW);
+ 
+ const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
+ 
+ vertexPositionBuffer.bind(gl);
+ gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);
+ 
+ gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
+ 
&lt;/span&gt;  function render() {
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the beginning of each frame we need to bind a framebuffer to tell webgl to render to a texture&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;  function render() {
&lt;span class="gi"&gt;+     gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ 
&lt;/span&gt;      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, 30]);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and after we rendered the scene to texture, we need to use our new program&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      renderSkybox(gl, viewMatrix, projectionMatrix);
      renderTerrain(gl, viewMatrix, projectionMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.useProgram(program);
+ 
&lt;/span&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Setup program attributes and uniforms&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;      gl.useProgram(program);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     vertexPositionBuffer.bind(gl);
+     gl.vertexAttribPointer(programInfo.attributeLocations.position, 2, gl.FLOAT, false, 0, 0);
+ 
+     gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
+ 
&lt;/span&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bind null framebuffer (this will make webgl render to canvas)&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;      gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ 
&lt;/span&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bind texture to use it as a source of color data&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
&lt;span class="gi"&gt;+     gl.bindTexture(gl.TEXTURE_2D, texture);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And issue a draw call&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
      gl.bindTexture(gl.TEXTURE_2D, texture);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.drawElements(gl.TRIANGLES, indexBuffer.data.length, gl.UNSIGNED_BYTE, 0);
+ 
&lt;/span&gt;      requestAnimationFrame(render);
  }
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since we're binding different texture after we render terrain and skybox, we need to re-bind textures in terrain and skybox programs&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;      await loadImage(textureSource).then((image) =&amp;gt; {
          const texture = createTexture(gl);
&lt;span class="gi"&gt;+         State.texture = texture;
+ 
&lt;/span&gt;          setImage(gl, texture, image);
&lt;span class="err"&gt;
&lt;/span&gt;          gl.generateMipmap(gl.TEXTURE_2D);
&lt;span class="err"&gt;
&lt;/span&gt;      setupAttributes(gl);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.bindTexture(gl.TEXTURE_2D, State.texture);
+ 
&lt;/span&gt;      gl.uniformMatrix4fv(State.programInfo.uniformLocations.viewMatrix, false, viewMatrix);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  export function render(gl, viewMatrix, projectionMatrix) {
      gl.useProgram(State.program);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.bindTexture(gl.TEXTURE_CUBE_MAP, State.texture);
+ 
&lt;/span&gt;      gl.uniformMatrix4fv(State.programInfo.uniformLocations.viewMatrix, false, viewMatrix);
      gl.uniformMatrix4fv(State.programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We need to create a depth buffer. Depth buffer is a render buffer (object which contains a data which came from fragmnt shader output)&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const depthBuffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+ 
&lt;/span&gt;  const vShader = gl.createShader(gl.VERTEX_SHADER);
  const fShader = gl.createShader(gl.FRAGMENT_SHADER);
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and setup renderbuffer to store depth info&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  const depthBuffer = gl.createRenderbuffer();
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, canvas.width, canvas.height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ 
&lt;/span&gt;  const vShader = gl.createShader(gl.VERTEX_SHADER);
  const fShader = gl.createShader(gl.FRAGMENT_SHADER);
&lt;span class="err"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now scene looks better, but only for a single frame, others seem to be drawn on top of previous. This happens because texture is&lt;br&gt;
n't cleared before next draw call&lt;/p&gt;

&lt;p&gt;We need to call a &lt;code&gt;gl.clear&lt;/code&gt; to clear the texture (clears currently bound framebuffer). This method accepts a bitmask which tells webgl which buffers to clear. We need to clear both color and depth buffer, so the mask is &lt;code&gt;gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 src/minecraft.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  function render() {
      gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ 
&lt;/span&gt;      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -30]);
      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, 30]);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: This also should be done before rendering to canvas if &lt;code&gt;preserveDrawingBuffer&lt;/code&gt; context argument is set to true&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can reuse our filter function from previous tutorial to make the whole scene black and white&lt;/p&gt;

&lt;p&gt;📄 src/shaders/filter.f.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  uniform sampler2D texture;
  uniform vec2 resolution;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ vec4 blackAndWhite(vec4 color) {
+     return vec4(vec3(1.0, 1.0, 1.0) * (color.r + color.g + color.b) / 3.0, color.a);
+ }
+ 
&lt;/span&gt;  void main() {
&lt;span class="gd"&gt;-     gl_FragColor = texture2D(texture, gl_FragCoord.xy / resolution);
&lt;/span&gt;&lt;span class="gi"&gt;+     gl_FragColor = blackAndWhite(texture2D(texture, gl_FragCoord.xy / resolution));
&lt;/span&gt;  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;Offscreen rendering (rendering to texture) might be used to apply different "post" effects like blur, water on camera, etc. We'll learn another useful usecase of offscreen rendering tomorrow&lt;/p&gt;

&lt;p&gt;Thanks for reading! 👋&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday26" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday26" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 25. Mipmaps</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Thu, 25 Jul 2019 13:05:23 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-25-mipmaps-33i</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-25-mipmaps-33i</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday25" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday25" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month&lt;/p&gt;

&lt;p&gt;Today we're going to learn one more webgl concept which might improve the quality of the final rendered image&lt;/p&gt;

&lt;p&gt;First we need to discuss how color is being read from texture.&lt;/p&gt;

&lt;p&gt;Let say we have a 1024x1024 image, but render only a 512x512 area on canvas. So each pixel in resulting image represents 4 pixels in original texture.&lt;/p&gt;

&lt;p&gt;Here's where &lt;code&gt;gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter)&lt;/code&gt; plays some role&lt;/p&gt;

&lt;p&gt;There are several algorithms on how to read a color from the texture&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gl.LINEAR&lt;/code&gt; - this one will read 4 pixels of original image and blend colors of 4 pixels to calculate final pixel color&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gl.NEARETS&lt;/code&gt; will just take the closest coordinate of the pixel from original image and use this color. While being more performant, this method has a lower quality&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both methods has it's caveats, especially when the size of area which need to be painted with texture is much smaller than original texture&lt;/p&gt;

&lt;p&gt;There is a special technique which allows to improve the quality and performance of rendering when dealing with textures. This special textures are called [mipmaps] – pre-calculated sequences of images, where each next image has a progressively smaller resolution. So when fragment shader reads a color from a texture, it takes the closest texture in size, and reads a color from it.&lt;/p&gt;

&lt;p&gt;In WebGL 1.0 mipmaps can only be generated for textures of "power-of-2" size (256x256, 512x512, 1024x1024 etc.)&lt;/p&gt;

&lt;p&gt;And that's how mipmap will look like for our dirt cube&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-tutor-assets.s3.eu-west-2.amazonaws.com%2Fmipmap.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgit-tutor-assets.s3.eu-west-2.amazonaws.com%2Fmipmap.jpg" alt="Mipmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't worry, you don't need to generate such a sequence for all your textures, this could be done automatically if your texture is a size of power of 2&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const State = {};
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ /**
+  *
+  * @param {WebGLRenderingContext} gl
+  */
&lt;/span&gt;  export async function prepare(gl) {
      const vShader = gl.createShader(gl.VERTEX_SHADER);
      const fShader = gl.createShader(gl.FRAGMENT_SHADER);
      await loadImage(textureSource).then((image) =&amp;gt; {
          const texture = createTexture(gl);
          setImage(gl, texture, image);
&lt;span class="gi"&gt;+ 
+         gl.generateMipmap(gl.TEXTURE_2D);
&lt;/span&gt;      });
&lt;span class="err"&gt;
&lt;/span&gt;      setupAttributes(gl);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in order to make GPU read a pixel color from mipmap, we need to specify &lt;code&gt;TEXTURE_MIN_FILTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;📄 src/minecraft-terrain.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;          setImage(gl, texture, image);
&lt;span class="err"&gt;
&lt;/span&gt;          gl.generateMipmap(gl.TEXTURE_2D);
&lt;span class="gi"&gt;+         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
&lt;/span&gt;      });
&lt;span class="err"&gt;
&lt;/span&gt;      setupAttributes(gl);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NEAREST_MIPMAP_LINEAR&lt;/code&gt; will choose the closest size mipmap and interpolate 4 pixels to get resulting color&lt;/p&gt;

&lt;p&gt;That's it for today!&lt;/p&gt;

&lt;p&gt;Thanks for reading, see you tomorrow 👋&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday25" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday25" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL month. Day 24. Combining terrain and skybox</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Wed, 24 Jul 2019 20:43:50 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-24-combining-terrain-and-skybox-kgo</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-24-combining-terrain-and-skybox-kgo</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3D25" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3D25" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month&lt;/p&gt;

&lt;p&gt;In previous tutorials we've rendered minecraft terrain and skybox, but in different examples. How do we combine them? WebGL allows to use multiple programs, so we can combine both examples with a slight refactor.&lt;/p&gt;

&lt;p&gt;Let's create a new entry point file &lt;code&gt;minecraft.js&lt;/code&gt; and assume &lt;code&gt;skybox.js&lt;/code&gt; and &lt;code&gt;minecraft-terrain.js&lt;/code&gt; export &lt;code&gt;prepare&lt;/code&gt; and &lt;code&gt;render&lt;/code&gt; functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prepare&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;prepareSkybox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;renderSkybox&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./skybox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prepare&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;prepareTerrain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;renderTerrain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./minecraft-terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we'll need to setup a canvas&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup camera matrices&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;142&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cameraPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cameraFocusPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cameraFocusPointMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;mat4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cameraFocusPointMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cameraFocusPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define a render function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;renderSkybox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;renderTerrain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and execute "preparation" code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;prepareSkybox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;prepareTerrain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to implement &lt;code&gt;prepare&lt;/code&gt; and &lt;code&gt;render&lt;/code&gt; functions of skybox and terrain&lt;/p&gt;

&lt;p&gt;Both functions will require access to shared state, like WebGL program, attributes and buffers, so let's create an object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// initialization code goes here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what's a "preparation" step?&lt;/p&gt;

&lt;p&gt;It's about creating program&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  export async function prepare(gl) {
&lt;span class="gi"&gt;+     const vShader = gl.createShader(gl.VERTEX_SHADER);
+     const fShader = gl.createShader(gl.FRAGMENT_SHADER);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     compileShader(gl, vShader, vShaderSource);
+     compileShader(gl, fShader, fShaderSource);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     const program = gl.createProgram();
+     State.program = program;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.attachShader(program, vShader);
+     gl.attachShader(program, fShader);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.linkProgram(program);
+     gl.useProgram(program);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     State.programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
&lt;/span&gt;  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Buffers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      gl.useProgram(program);
&lt;span class="err"&gt;
&lt;/span&gt;      State.programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     const cube = new Object3D(cubeObj, [0, 0, 0], [0, 0, 0]);
+     State.vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cube.vertices, gl.STATIC_DRAW);
&lt;/span&gt;  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Textures&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      const cube = new Object3D(cubeObj, [0, 0, 0], [0, 0, 0]);
      State.vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cube.vertices, gl.STATIC_DRAW);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     await Promise.all([
+         loadImage(rightTexture),
+         loadImage(leftTexture),
+         loadImage(upTexture),
+         loadImage(downTexture),
+         loadImage(backTexture),
+         loadImage(frontTexture),
+     ]).then((images) =&amp;gt; {
+         State.texture = gl.createTexture();
+         gl.bindTexture(gl.TEXTURE_CUBE_MAP, State.texture);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+         gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+         gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+         gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+         gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+         images.forEach((image, index) =&amp;gt; {
+             gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + index, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+         });
+     });
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and setting up attributes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;              gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X   index, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
          });
      });
&lt;span class="gi"&gt;+     setupAttributes(gl);
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a separate function to setup attributes because we'll need to do this in render function as well. Attributes share the state between different programs, so we'll need to setup them properly each time we use different program&lt;/p&gt;

&lt;p&gt;&lt;code&gt;setupAttributes&lt;/code&gt; looks like this for &lt;code&gt;skybox&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vertexAttribPointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributeLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we need a render function which will pass view and projection matrices to uniforms and issue a draw call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniformMatrix4fv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniformLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniformMatrix4fv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniformLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setupAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This refactor is pretty straightforward, as it requires only moving pieces of code to necessary functions, so this steps will look the same for &lt;code&gt;minecraft-terrain&lt;/code&gt;, with one exception&lt;/p&gt;

&lt;p&gt;We're using &lt;code&gt;ANGLE_instanced_arrays&lt;/code&gt; extension to render terrain, which sets up &lt;code&gt;divisorAngle&lt;/code&gt;. As attributes share the state between programs, we'll need to "reset" those divisor angles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resetDivisorAngles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vertexAttribDivisorANGLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributeLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modelMatrix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and call this function after a draw call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setupAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniformMatrix4fv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniformLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniformMatrix4fv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;programInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniformLocations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawArraysInstancedANGLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;resetDivisorAngles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does resulting code actually work?&lt;/p&gt;

&lt;p&gt;Unfortunatelly no 😢&lt;br&gt;
The issue is that we render the skybox inisde the cube which is smaller than our terrain, but we can fix it with a single change in skybox vertex shader&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  attribute vec3 position;
  varying vec3 vTexCoord;
&lt;span class="err"&gt;
&lt;/span&gt;  uniform mat4 projectionMatrix;
  uniform mat4 viewMatrix;
&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
      vTexCoord = position;
&lt;span class="gd"&gt;-     gl_Position = projectionMatrix * viewMatrix * vec4(position, 1);
&lt;/span&gt;&lt;span class="gi"&gt;+     gl_Position = projectionMatrix * viewMatrix * vec4(position, 0.01);
&lt;/span&gt;  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By changing the 4th argument, we'll scale our skybox by 100 times (the magic of homogeneous coordinates).&lt;/p&gt;

&lt;p&gt;After this change the world looks ok, until we try to look at the farthest "edge" of our world cube. Skybox isn't rendered there 😢&lt;/p&gt;

&lt;p&gt;This happens because of the &lt;code&gt;zFar&lt;/code&gt; argument passed to projection matrix&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  const projectionMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;  mat4.lookAt(viewMatrix, [0, 0, 0], [0, 0, -1], [0, 1, 0]);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- mat4.perspective(projectionMatrix, (Math.PI / 360) * 90, canvas.width / canvas.height, 0.01, 100);
&lt;/span&gt;&lt;span class="gi"&gt;+ mat4.perspective(projectionMatrix, (Math.PI / 360) * 90, canvas.width / canvas.height, 0.01, 142);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distance to the farthest edge is &lt;code&gt;Math.sqrt(size ** 2 + size ** 2)&lt;/code&gt;, which is &lt;code&gt;141.4213562373095&lt;/code&gt;, so we can just pass &lt;code&gt;142&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;Thanks for reading, see you tomorrow 👋&lt;/p&gt;




&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3D25" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3D25" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 23. Skybox in WebGL</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Tue, 23 Jul 2019 17:46:20 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-23-skybox-in-webgl-1eig</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-23-skybox-in-webgl-1eig</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday23" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday23" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month.&lt;/p&gt;

&lt;p&gt;In previous tutorials we've rendered objects without any surroundings, but what if we want to add sky to our scene?&lt;/p&gt;

&lt;p&gt;There's a special texture type which mught help us with it&lt;/p&gt;

&lt;p&gt;We can treat our scene as a giant cube where camera is always in the center of this cube.&lt;br&gt;
So all we need it render this cube and apply a texture, like below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvosyky2c1zff4xv9o3of.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvosyky2c1zff4xv9o3of.png" alt="Skybox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vertex shader will have vertex positions and texCoord attribute, view and projection matrix uniforms. We don't need model matrix as our "world" cube is static&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.v.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;

&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;vTexCoord&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;projectionMatrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If our cube vertices coordinates are in &lt;code&gt;[-1..1]&lt;/code&gt; range, we can use this coordinates as texture coordinates directly&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.v.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  uniform mat4 viewMatrix;
&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
&lt;span class="gd"&gt;- 
&lt;/span&gt;&lt;span class="gi"&gt;+     vTexCoord = position;
&lt;/span&gt;  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And to calculate position of transformed vertex we need to multiply vertex position, view matrix and projection matrix&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.v.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;  void main() {
      vTexCoord = position;
&lt;span class="gi"&gt;+     gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
&lt;/span&gt;  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Fragment shader should have a vTexCoord varying to receive tex coords from vertex shader&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.f.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;

&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;vTexCoord&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and a special type of texture – sampler cube&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.f.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  precision mediump float;
&lt;span class="err"&gt;
&lt;/span&gt;  varying vec3 vTexCoord;
&lt;span class="gi"&gt;+ uniform samplerCube skybox;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
&lt;span class="gd"&gt;- 
&lt;/span&gt;  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and all we need to calculate fragment color is to read color from cubemap texture&lt;/p&gt;

&lt;p&gt;📄 src/shaders/skybox.f.glsl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  uniform samplerCube skybox;
&lt;span class="err"&gt;
&lt;/span&gt;  void main() {
&lt;span class="gi"&gt;+     gl_FragColor = textureCube(skybox, vTexCoord);
&lt;/span&gt;  }
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As usual we need to get a canvas reference, webgl context, and make canvas fullscreen&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webgl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Setup webgl program&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gi"&gt;+ import vShaderSource from './shaders/skybox.v.glsl';
+ import fShaderSource from './shaders/skybox.f.glsl';
+ 
+ import { compileShader, setupShaderInput } from './gl-helpers';
+ 
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
&lt;span class="err"&gt;

&lt;/span&gt;  canvas.style.width = `${width}px`;
  canvas.style.height = `${height}px`;
&lt;span class="gi"&gt;+ 
+ const vShader = gl.createShader(gl.VERTEX_SHADER);
+ const fShader = gl.createShader(gl.FRAGMENT_SHADER);
+ 
+ compileShader(gl, vShader, vShaderSource);
+ compileShader(gl, fShader, fShaderSource);
+ 
+ const program = gl.createProgram();
+ 
+ gl.attachShader(program, vShader);
+ gl.attachShader(program, fShader);
+ 
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ 
+ const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create cube object and setup buffer for vertex positions&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  import fShaderSource from './shaders/skybox.f.glsl';
&lt;span class="err"&gt;
&lt;/span&gt;  import { compileShader, setupShaderInput } from './gl-helpers';
&lt;span class="gi"&gt;+ import { Object3D } from './Object3D';
+ import { GLBuffer } from './GLBuffer';
+ 
+ import cubeObj from '../assets/objects/cube.obj';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
  gl.useProgram(program);
&lt;span class="err"&gt;
&lt;/span&gt;  const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
&lt;span class="gi"&gt;+ 
+ const cube = new Object3D(cubeObj, [0, 0, 0], [0, 0, 0]);
+ const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cube.vertices, gl.STATIC_DRAW);
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Setup position attribute&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;  const cube = new Object3D(cubeObj, [0, 0, 0], [0, 0, 0]);
  const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cube.vertices, gl.STATIC_DRAW);
&lt;span class="gi"&gt;+ 
+ vertexBuffer.bind(gl);
+ gl.vertexAttribPointer(programInfo.attributeLocations.position, 3, gl.FLOAT, false, 0, 0);
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Setup view, projection matrices, pass values to uniforms and set viewport&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  import { GLBuffer } from './GLBuffer';
&lt;span class="err"&gt;
&lt;/span&gt;  import cubeObj from '../assets/objects/cube.obj';
&lt;span class="gi"&gt;+ import { mat4 } from 'gl-matrix';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
&lt;span class="err"&gt;
&lt;/span&gt;  vertexBuffer.bind(gl);
  gl.vertexAttribPointer(programInfo.attributeLocations.position, 3, gl.FLOAT, false, 0, 0);
&lt;span class="gi"&gt;+ 
+ const viewMatrix = mat4.create();
+ const projectionMatrix = mat4.create();
+ 
+ mat4.lookAt(viewMatrix, [0, 0, 0], [0, 0, -1], [0, 1, 0]);
+ 
+ mat4.perspective(projectionMatrix, (Math.PI / 360) * 90, canvas.width / canvas.height, 0.01, 100);
+ 
+ gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
+ gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
+ 
+ gl.viewport(0, 0, canvas.width, canvas.height);
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And define a function which will render our scene&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix, false, projectionMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="gi"&gt;+ 
+ function frame() {
+     gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
+ 
+     requestAnimationFrame(frame);
+ }
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the fun part. Texture for each side of the cube should be stored in separate file, so we need to laod all images. &lt;a href="http://www.custommapmakers.org/skyboxes.php" rel="noopener noreferrer"&gt;Check out this site for other textures&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  import vShaderSource from './shaders/skybox.v.glsl';
  import fShaderSource from './shaders/skybox.f.glsl';
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- import { compileShader, setupShaderInput } from './gl-helpers';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { compileShader, setupShaderInput, loadImage } from './gl-helpers';
&lt;/span&gt;  import { Object3D } from './Object3D';
  import { GLBuffer } from './GLBuffer';
&lt;span class="err"&gt;
&lt;/span&gt;  import cubeObj from '../assets/objects/cube.obj';
  import { mat4 } from 'gl-matrix';
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ import rightTexture from '../assets/images/skybox/right.JPG';
+ import leftTexture from '../assets/images/skybox/left.JPG';
+ import upTexture from '../assets/images/skybox/up.JPG';
+ import downTexture from '../assets/images/skybox/down.JPG';
+ import backTexture from '../assets/images/skybox/back.JPG';
+ import frontTexture from '../assets/images/skybox/front.JPG';
+ 
&lt;/span&gt;  const canvas = document.querySelector('canvas');
  const gl = canvas.getContext('webgl');
&lt;span class="err"&gt;

&lt;/span&gt;      requestAnimationFrame(frame);
  }
&lt;span class="gi"&gt;+ 
+ Promise.all([
+     loadImage(rightTexture),
+     loadImage(leftTexture),
+     loadImage(upTexture),
+     loadImage(downTexture),
+     loadImage(backTexture),
+     loadImage(frontTexture),
+ ]).then((images) =&amp;gt; {
+     frame();
+ });
&lt;/span&gt;&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we need to create a webgl texture&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      loadImage(backTexture),
      loadImage(frontTexture),
  ]).then((images) =&amp;gt; {
&lt;span class="gi"&gt;+     const texture = gl.createTexture();
+ 
&lt;/span&gt;      frame();
  });
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And pass a special texture type to bind method – &lt;code&gt;gl.TEXTURE_CUBE_MAP&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      loadImage(frontTexture),
  ]).then((images) =&amp;gt; {
      const texture = gl.createTexture();
&lt;span class="gi"&gt;+     gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      frame();
  });
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we need to setup texture&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      const texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ 
&lt;/span&gt;      frame();
  });
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and upload each image to gpu&lt;/p&gt;

&lt;p&gt;Targets are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_POSITIVE_X&lt;/code&gt; – right&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_NEGATIVE_X&lt;/code&gt; – left&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_POSITIVE_Y&lt;/code&gt; – top&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_NEGATIVE_Y&lt;/code&gt; – bottom&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_POSITIVE_Z&lt;/code&gt; – front&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;gl.TEXTURE_CUBE_MAP_NEGATIVE_Z&lt;/code&gt; – back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since all these values are integers, we can iterate over all images and add image index to &lt;code&gt;TEXTURE_CUBE_MAP_POSITIVE_X&lt;/code&gt; target&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;      gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     images.forEach((image, index) =&amp;gt; {
+         gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + index, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+     });
+ 
&lt;/span&gt;      frame();
  });
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and finally let's reuse the code from &lt;a href="https://dev.to/lesnitsky/webgl-month-day-21-rendering-a-minecraft-terrain-24b5"&gt;previous tutorial&lt;/a&gt; to implement camera rotation animation&lt;/p&gt;

&lt;p&gt;📄 src/skybox.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  import { GLBuffer } from './GLBuffer';
&lt;span class="err"&gt;
&lt;/span&gt;  import cubeObj from '../assets/objects/cube.obj';
&lt;span class="gd"&gt;- import { mat4 } from 'gl-matrix';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { mat4, vec3 } from 'gl-matrix';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  import rightTexture from '../assets/images/skybox/right.JPG';
  import leftTexture from '../assets/images/skybox/left.JPG';
&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const cameraPosition = [0, 0, 0];
+ const cameraFocusPoint = vec3.fromValues(0, 0, 1);
+ const cameraFocusPointMatrix = mat4.create();
+ 
+ mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
+ 
&lt;/span&gt;  function frame() {
&lt;span class="gi"&gt;+     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, -1]);
+     mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
+     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [0, 0, 1]);
+ 
+     mat4.getTranslation(cameraFocusPoint, cameraFocusPointMatrix);
+ 
+     mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
+     gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
+ 
&lt;/span&gt;      gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
&lt;span class="err"&gt;
&lt;/span&gt;      requestAnimationFrame(frame);
&lt;span class="err"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's it, we now have a skybox which makes scene look more impressive 😎&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;See you tomorrow 👋&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday23" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday23" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL Month. Day 22. Reducing number of WebGL calls by 5000 times</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Mon, 22 Jul 2019 20:25:11 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-22-reducing-number-of-webgl-calls-by-5000-times-3a4j</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-22-reducing-number-of-webgl-calls-by-5000-times-3a4j</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday22" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday22" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/lesnitsky/webgl-month-day-21-rendering-a-minecraft-terrain-24b5"&gt;Yesterday&lt;/a&gt; we've rendered minecraft terrain, but implementation wasn't optimal. We had to issue two gl calls for each block. One to update model matrix uniform, another to issue a draw call. There is a way to render the whole scene with a SINGLE call, so that way we'll reduce number of calls by 5000 times 🤯.&lt;/p&gt;

&lt;p&gt;These technique is called WebGL instancing. Our cubes share the same vertex and tex coord data, the only difference is model matrix. Instead of passing it as uniform we can define an attribute&lt;/p&gt;

&lt;p&gt;📄 src/shaders/3d-textured.v.glsl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  attribute vec3 position;
  attribute vec2 texCoord;
&lt;span class="gi"&gt;+ attribute mat4 modelMatrix;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- uniform mat4 modelMatrix;
&lt;/span&gt;  uniform mat4 viewMatrix;
  uniform mat4 projectionMatrix;
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Matrix attributes are actually a number of &lt;code&gt;vec4&lt;/code&gt; attributes, so if &lt;code&gt;mat4&lt;/code&gt; attribute location is &lt;code&gt;0&lt;/code&gt;, we'll have 4 separate attributes with locations &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;. Our &lt;code&gt;setupShaderInput&lt;/code&gt; helper doesn't support these, so we'll need to enable each attribute manually&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const programInfo = setupShaderInput(gl, program, vShaderSource, fShaderSource);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ for (let i = 0; i &amp;lt; 4; i++) {
+     gl.enableVertexAttribArray(programInfo.attributeLocations.modelMatrix + i);
+ }
+ 
&lt;/span&gt;  const cube = new Object3D(cubeObj, [0, 0, 0], [1, 0, 0]);
&lt;span class="err"&gt;
&lt;/span&gt;  const vertexBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, cube.vertices, gl.STATIC_DRAW);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to define a Float32Array for matrices data. The size is &lt;code&gt;100 * 100&lt;/code&gt; (size of our world) &lt;code&gt;* 4 * 4&lt;/code&gt; (dimensions of the model matrix)&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const matrices = [];
&lt;/span&gt;&lt;span class="gi"&gt;+ const matrices = new Float32Array(100 * 100 * 4 * 4);
&lt;/span&gt;  const rotationMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;  for (let i = -50; i &amp;lt; 50; i++) {
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To save resources we can use a single model matrix for all cubes while filling matrices array with data&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;  const matrices = new Float32Array(100 * 100 * 4 * 4);
&lt;span class="gi"&gt;+ const modelMatrix = mat4.create();
&lt;/span&gt;  const rotationMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;  for (let i = -50; i &amp;lt; 50; i++) {
      for (let j = -50; j &amp;lt; 50; j++) {
&lt;span class="gd"&gt;-         const matrix = mat4.create();
- 
&lt;/span&gt;          const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2];
&lt;span class="gd"&gt;-         mat4.fromTranslation(matrix, position);
&lt;/span&gt;&lt;span class="gi"&gt;+         mat4.fromTranslation(modelMatrix, position);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;          mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]);
&lt;span class="gd"&gt;-         mat4.multiply(matrix, matrix, rotationMatrix);
&lt;/span&gt;&lt;span class="gi"&gt;+         mat4.multiply(modelMatrix, modelMatrix, rotationMatrix);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;          matrices.push(matrix);
      }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also need a counter to know the offset at the matrices Float32Array to write data to an appropriate location&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  const modelMatrix = mat4.create();
  const rotationMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ let cubeIndex = 0;
+ 
&lt;/span&gt;  for (let i = -50; i &amp;lt; 50; i++) {
      for (let j = -50; j &amp;lt; 50; j++) {
          const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2];
          mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]);
          mat4.multiply(modelMatrix, modelMatrix, rotationMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-         matrices.push(matrix);
&lt;/span&gt;&lt;span class="gi"&gt;+         modelMatrix.forEach((value, index) =&amp;gt; {
+             matrices[cubeIndex * 4 * 4 + index] = value;
+         });
+ 
+         cubeIndex++;
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need a matrices gl buffer&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      }
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const matricesBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, matrices, gl.STATIC_DRAW);
+ 
&lt;/span&gt;  const cameraPosition = [0, 10, 0];
  const cameraFocusPoint = vec3.fromValues(30, 0, 0);
  const cameraFocusPointMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and setup attribute pointer using stride and offset, since our buffer is interleaved. &lt;a href="https://dev.to/lesnitsky/webgl-month-day-5-interleaved-buffers-2k9a"&gt;Learn more about interleaved buffers here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const matricesBuffer = new GLBuffer(gl, gl.ARRAY_BUFFER, matrices, gl.STATIC_DRAW);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const offset = 4 * 4; // 4 floats 4 bytes each
+ const stride = offset * 4; // 4 rows of 4 floats
+ 
+ for (let i = 0; i &amp;lt; 4; i++) {
+     gl.vertexAttribPointer(programInfo.attributeLocations.modelMatrix + i, 4, gl.FLOAT, false, stride, i * offset);
+ }
+ 
&lt;/span&gt;  const cameraPosition = [0, 10, 0];
  const cameraFocusPoint = vec3.fromValues(30, 0, 0);
  const cameraFocusPointMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instancing itself isn't supported be webgl 1 out of the box, but available via extension, so we need to get it&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  const offset = 4 * 4; // 4 floats 4 bytes each
  const stride = offset * 4; // 4 rows of 4 floats
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const ext = gl.getExtension('ANGLE_instanced_arrays');
+ 
&lt;/span&gt;  for (let i = 0; i &amp;lt; 4; i++) {
      gl.vertexAttribPointer(programInfo.attributeLocations.modelMatrix + i, 4, gl.FLOAT, false, stride, i * offset);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically what this extension does, is helps us avoid repeating vertex positions and texture coordinates for each cube, since these are the same. By using instancing we tell WebGL to render N instances of objects, reusing some attribute data for each object, and getting "unique" data for other attributes. To specify which attributes contain data for each object, we need to call &lt;code&gt;vertexAttribDivisorANGLE(location, divisor)&lt;/code&gt; method of the extension.&lt;/p&gt;

&lt;p&gt;Divisor is used to determine how to read data from attributes filled with data for each object.&lt;/p&gt;

&lt;p&gt;Our modelMatrix attribute has a matrix for each object, so divisor should be &lt;code&gt;1&lt;/code&gt;.&lt;br&gt;
We can use modelMarix &lt;code&gt;A&lt;/code&gt; for objects &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt; for objects &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;3&lt;/code&gt; – in this case divisor is &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our case it is &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  for (let i = 0; i &amp;lt; 4; i++) {
      gl.vertexAttribPointer(programInfo.attributeLocations.modelMatrix + i, 4, gl.FLOAT, false, stride, i * offset);
&lt;span class="gi"&gt;+     ext.vertexAttribDivisorANGLE(programInfo.attributeLocations.modelMatrix + i, 1);
&lt;/span&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  const cameraPosition = [0, 10, 0];
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we can get read of iteration over all matrices, and use a single  call. However we should call it on the instance of extension instead of gl itself. The last argument should be the number of instances we want to render&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
      gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     matrices.forEach((matrix) =&amp;gt; {
-         gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
- 
-         gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
-     });
&lt;/span&gt;&lt;span class="gi"&gt;+     ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, vertexBuffer.data.length / 3, 100 * 100);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      requestAnimationFrame(frame);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! We just reduced number of gl calls by 5000 times 🎉!&lt;/p&gt;

&lt;p&gt;WebGL instancing extension is widely support, so don't hesitate to use it whenever it makes sense.&lt;/p&gt;

&lt;p&gt;Typical case – need to render a lot of the same objects but with different locations, colors and other type of "attributes"&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;br&gt;
See you tomorrow 👋&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday22" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday22" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>WebGL month. Day 21. Rendering a minecraft terrain</title>
      <dc:creator>Andrei Lesnitsky</dc:creator>
      <pubDate>Sun, 21 Jul 2019 16:40:55 +0000</pubDate>
      <link>https://dev.to/lesnitsky/webgl-month-day-21-rendering-a-minecraft-terrain-24b5</link>
      <guid>https://dev.to/lesnitsky/webgl-month-day-21-rendering-a-minecraft-terrain-24b5</guid>
      <description>&lt;p&gt;This is a series of blog posts related to WebGL. New post will be available every day&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday21" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday21" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey 👋&lt;/p&gt;

&lt;p&gt;Welcome to WebGL month.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/lesnitsky/webgl-month-day-20-rendering-a-minecraft-dirt-cube-5ag3"&gt;Yesterday&lt;/a&gt; we rendered a single minecraft dirt cube, let's render a terrain today!&lt;/p&gt;

&lt;p&gt;We'll need to store each block position in separate transform matrix&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const matrices = [];
+ 
&lt;/span&gt;  function frame() {
      mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create 10k blocks iteration over x and z axis from -50 to 50&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;  const matrices = [];
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ for (let i = -50; i &amp;lt; 50; i++) {
+     for (let j = -50; j &amp;lt; 50; j++) {
+         const matrix = mat4.create();
+     }
+ }
+ 
&lt;/span&gt;  function frame() {
      mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each block is a size of 2 (vertex coordinates are in [-1..1] range) so positions should be divisible by two&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  for (let i = -50; i &amp;lt; 50; i++) {
      for (let j = -50; j &amp;lt; 50; j++) {
          const matrix = mat4.create();
&lt;span class="gi"&gt;+ 
+         const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2];
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to create a transform matrix. Let's use &lt;code&gt;ma4.fromTranslation&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;          const matrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;          const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2];
&lt;span class="gi"&gt;+         mat4.fromTranslation(matrix, position);
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also rotate each block around Y axis to make terrain look more random&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  gl.viewport(0, 0, canvas.width, canvas.height);
&lt;span class="err"&gt;
&lt;/span&gt;  const matrices = [];
&lt;span class="gi"&gt;+ const rotationMatrix = mat4.create();
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  for (let i = -50; i &amp;lt; 50; i++) {
      for (let j = -50; j &amp;lt; 50; j++) {
&lt;span class="err"&gt;
&lt;/span&gt;          const position = [i * 2, (Math.floor(Math.random() * 2) - 1) * 2, j * 2];
          mat4.fromTranslation(matrix, position);
&lt;span class="gi"&gt;+ 
+         mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]);
+         mat4.multiply(matrix, matrix, rotationMatrix);
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and finally push matrix of each block to matrices collection&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;
&lt;/span&gt;          mat4.fromRotation(rotationMatrix, Math.PI * Math.round(Math.random() * 4), [0, 1, 0]);
          mat4.multiply(matrix, matrix, rotationMatrix);
&lt;span class="gi"&gt;+ 
+         matrices.push(matrix);
&lt;/span&gt;      }
  }
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our blocks are static, we don't need a rotation transform in each frame&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  function frame() {
&lt;span class="gd"&gt;-     mat4.rotateY(cube.modelMatrix, cube.modelMatrix, Math.PI / 180);
- 
&lt;/span&gt;      gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, cube.modelMatrix);
      gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll need to iterate over matrices collection and issue a draw call for each cube with its transform matrix passed to uniform&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  }
&lt;span class="err"&gt;
&lt;/span&gt;  function frame() {
&lt;span class="gd"&gt;-     gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, cube.modelMatrix);
-     gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
&lt;/span&gt;&lt;span class="gi"&gt;+     matrices.forEach((matrix) =&amp;gt; {
+         gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
+         gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
&lt;/span&gt;&lt;span class="gi"&gt;+         gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
+     });
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      requestAnimationFrame(frame);
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create an animation of rotating camera. Camera has a position and a point where it is pointed. So to implement this, we need to rotate focus point around camera position. Let's first get rid of static view matrix&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  const viewMatrix = mat4.create();
  const projectionMatrix = mat4.create();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- mat4.lookAt(viewMatrix, [0, 4, -7], [0, 0, 0], [0, 1, 0]);
- 
&lt;/span&gt;  mat4.perspective(projectionMatrix, (Math.PI / 360) * 90, canvas.width / canvas.height, 0.01, 100);
&lt;span class="err"&gt;
&lt;/span&gt;  gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define camera position, camera focus point vector and focus point transform matrix&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import { mat4 } from 'gl-matrix';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { mat4, vec3 } from 'gl-matrix';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  import vShaderSource from './shaders/3d-textured.v.glsl';
  import fShaderSource from './shaders/3d-textured.f.glsl';
      }
  }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const cameraPosition = [0, 10, 0];
+ const cameraFocusPoint = vec3.fromValues(30, 0, 0);
+ const cameraFocusPointMatrix = mat4.create();
+ 
+ mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
+ 
&lt;/span&gt;  function frame() {
      matrices.forEach((matrix) =&amp;gt; {
          gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our camera is located in 0.0.0, so we need to translate camera focus point to 0.0.0, rotate it, and translate back to original position&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  mat4.fromTranslation(cameraFocusPointMatrix, cameraFocusPoint);
&lt;span class="err"&gt;
&lt;/span&gt;  function frame() {
&lt;span class="gi"&gt;+     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [-30, 0, 0]);
+     mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
+     mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [30, 0, 0]);
+ 
&lt;/span&gt;      matrices.forEach((matrix) =&amp;gt; {
          gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
          gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final step – update view matrix uniform&lt;/p&gt;

&lt;p&gt;📄 src/3d-textured.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;      mat4.rotateY(cameraFocusPointMatrix, cameraFocusPointMatrix, Math.PI / 360);
      mat4.translate(cameraFocusPointMatrix, cameraFocusPointMatrix, [30, 0, 0]);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     mat4.getTranslation(cameraFocusPoint, cameraFocusPointMatrix);
+ 
+     mat4.lookAt(viewMatrix, cameraPosition, cameraFocusPoint, [0, 1, 0]);
+     gl.uniformMatrix4fv(programInfo.uniformLocations.viewMatrix, false, viewMatrix);
+ 
&lt;/span&gt;      matrices.forEach((matrix) =&amp;gt; {
          gl.uniformMatrix4fv(programInfo.uniformLocations.modelMatrix, false, matrix);
&lt;span class="gd"&gt;-         gl.uniformMatrix4fv(programInfo.uniformLocations.normalMatrix, false, cube.normalMatrix);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;          gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.data.length / 3);
      });
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;This approach is not very performant though, as we're issuing 2 gl calls for each object, so it is a 20k of gl calls each frame. GL calls are expensive, so we'll need to reduce this number. We'll learn a great technique tomorrow!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fgithub%2Fstars%2Flesnitsky%2Fwebgl-month.svg%3Fstyle%3Dsocial%26hash%3Dday21" alt="GitHub stars"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/lesnitsky_a" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Ftwitter%2Ffollow%2Flesnitsky_a.svg%3Flabel%3DFollow%2520me%26style%3Dsocial%26hash%3Dday21" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/gwiSeH" rel="noopener noreferrer"&gt;Join mailing list&lt;/a&gt; to get new posts right to your inbox&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/webgl-month" rel="noopener noreferrer"&gt;Source code available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lesnitsky/git-tutor" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62mvzgx3aeqpxkkzqyre.png" alt="Git Tutor Logo"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
  </channel>
</rss>
