Making WebGL Clouds with Three.JS and HTML5 – WebGL Example

by | Jun 24, 2016

HTML5 was a huge step forward in web development. With much-needed quality-of-life improvements, consistent cross-platform functionality, and new APIs, HTML5’s capabilities are immense. Today we will be looking into WebGL rendering with HTML5. three.js allows easy implementation of WebGL/canvas. Let’s jump right into this WebGL example by making some animated clouds! Full page demo here.
I’ve always been a fan of Bob Ross, and I want help continue his legacy of painting happy little clouds by creating happy little clouds in HTML5. TL;DR? Results are here.
Bob Ross, painting some wonderful clouds.
Bob Ross still lives on in the heart of fluffy clouds and happy little trees everywhere.

Getting Started

In this article, we are using three.js for 3D rendering, so make sure you have the library. First, we want to include three.js and style our page. Nothing special, just setting a blue background.
<style type="text/css">
body{
	background-color:#016ece;
	margin:0;
	padding:0;
	overflow: hidden;
}
</style>
<script type="text/javascript" src="js/three.min.js"></script>
Our web page should be nice and blue now. A blue sky is the first step in painting beautiful clouds, but now we need some clouds to display. Since we’re going to programmatically create clouds, our cloud particles do not need to be complex. By randomizing scale, opacity, and rotation, we can turn simple cloud particles into large, amorphous, randomly generated clouds.

This is the image that will be used to generate the clouds.
How will this be accomplished?
With this setup, I will be creating a plane with a camera, randomly generate 600 cloud sprites, and constantly rotate the camera. This gives the illusion of floating through clouds. In this instance, sprites are 2d images projected onto a 3d scene. Sprites always face the camera. And since the cloud particles are random in nature (random scaling, rotation, positioning, opacity), 3d effects are exaggerated, but it does make for quite a pretty experience.
Without further ado, let’s jump into the code in which we can bring our clouds to life.
<pre><code><p>// Creating our globals. var container; var camera, scene, renderer; var mesh, geometry, material; init(); function init() { // First, create a container to put our canvas in. container = document.createElement('div'); document.body.appendChild(container); // Create a canvas. var canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = window.innerHeight; // Draw the sky gradient. var context = canvas.getContext('2d'); var gradient = context.createLinearGradient(0, 0, 0, canvas.height); gradient.addColorStop(0, "#094b86"); gradient.addColorStop(0.5, "#016ece"); context.fillStyle = gradient; context.fillRect(0, 0, canvas.width, canvas.height); container.style.background = 'url(' + canvas.toDataURL('image/png') + ')'; container.style.backgroundSize = '32px 100%'; // Create a new THREE camera - POV of 30, aspect ratio of window's dimensions, and near/far frustum. camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 3000); // Initialize the camera's position in the center of the clouds. camera.position.x = 1000; camera.position.y = 250; camera.position.z = 1000; // Randomize the camera rotation. camera.rotation.y += Math.random()*-720; camera.rotation.z -= Math.random()*720; // Create a scene object. scene = new THREE.Scene(); // Create a geoemtry object. geometry = new THREE.Geometry(); // Create a texture for the shader material. var texture = THREE.ImageUtils.loadTexture('cloud.png', null, animate); texture.magFilter = THREE.LinearMipMapLinearFilter; texture.minFilter = THREE.LinearMipMapLinearFilter; // Create a blue fog. var fog = new THREE.Fog(0x3390ed, -100, 5000); material = new THREE.ShaderMaterial({ uniforms: { "map": { type: "t", value: texture }, "fogColor" : { type: "c", value: fog.color }, "fogNear" : { type: "f", value: fog.near }, "fogFar" : { type: "f", value: fog.far }, }, depthWrite: false, depthTest: false, transparent: true }); // Create a 3d plane. var plane = new THREE.Mesh(new THREE.PlaneGeometry(64,64)); // Returns random position value which does not get too close to the camera. function ranCloud() { val = Math.random()*2000-100; while(val > 900 && val < 1100){ ranCloud(); } return val; } // Randomly generate clouds. for (var i = 0; i < 600; i++) { // Load texture. var cloudTex = THREE.ImageUtils.loadTexture('cloud.png'); // Create material. var cloudMat = new THREE.SpriteMaterial({ map: cloudTex, useScreenCoordinates: false, alignment: THREE.SpriteAlignment.topLeft, transparent:true, opacity:Math.random()*.9+.1 }); // Create sprite. var sprite = new THREE.Sprite(cloudMat); // Randomize sprite position. sprite.position.set(ranCloud(), ranCloud()*.4, ranCloud()); // Randomize sprite scale. var scale = Math.random()*500; sprite.scale.set(scale, scale, scale); // Randomize sprite rotation. sprite.rotation = Math.random()*360; // Add the sprite to the scene. scene.add(sprite); } // Create a mesh mesh = new THREE.Mesh(geometry, material); mesh.position.z = 1000; scene.add(mesh); // Lastly, create a renderer to display our scene. renderer = new THREE.WebGLRenderer({ antialias: false }); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); // Create a resize listener. window.addEventListener('resize', onWindowResize, false); } function onWindowResize(event) { // On resize, the camera's aspect ratio should be updated. camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); // Update the renderer's size. renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { // Animate the frame. requestAnimationFrame(animate); // Apply constant rotation to the camera. camera.rotation.y += .0004; camera.rotation.z -= .0001; // Render the scene. renderer.render(scene, camera); }</p></code></pre>
Final Results
<iframe style="width:100%;height:85vh;" src="/j/index.html"></iframe>
There we have it – animated WebGL HTML5 clouds. It’s like floating in the sky, but it’s all a lie brought forth by amazing HTML5 technology. Who would have thought? Join us later in building native HTML5 apps in Android and iOS!

ABOUT THE AUTHOR

JESSE WALLACE

Jesse is a full stack web developer and ninja. His elite skills and wizardry is utilized to create ethereal masterpieces of magical wonder.

Tags: , , , , , ,