Sprite of 3.js 07-02 formats particles with Canvas canvas

Keywords: JQuery Three.js Javascript Attribute

After the introduction of the previous article, I believe you have a basic general understanding of 3.js. But the uninteresting particles are ugly. This article describes how to format Sprite particles through Canvas canvas. My friends who read my last blog should remember that the SpriteMaterial property has an attribute called "map", which is actually a THREE.Texture object. With this THREE.Texture object, we can format our particles using Canvas canvas.

To format our particles through Canvas canvas, you must be familiar with the basic usage of the Canvas API. Let's start with a complete example. The code is as follows:

<!DOCTYPE html>
<html>
<head>
    <title>Example 07.01 - Particles</title>
	<script src="../build/three.js"></script>
	<script src="../build/js/controls/OrbitControls.js"></script>
	<script src="../build/js/libs/stats.min.js"></script>
	<script src="../build/js/libs/dat.gui.min.js"></script>
	<script src="../jquery/jquery-3.2.1.min.js"></script>
    <style>
        body {
            /* Set margin to 0 and overflow to hidden to complete the page style */
            margin: 0;
            overflow: hidden;
        }
		/* Styles of Statistical Objects */
		#Stats-output {
			position: absolute;
			left: 0px;
			top: 0px;
		}
    </style>
</head>
<body>

<!-- Be used for WebGL Output Div -->
<div id="webgl-output"></div>
<!-- Used for statistics FPS Output Div -->
<div id="stats-output"></div>

<!-- Function Three.js Example Javascript Code -->
<script type="text/javascript">

	var scene;
	var camera;
	var render;
	var webglRender;
	//var canvasRender;
	var controls;
	var stats;
	var guiParams;
	
	var ground;
	var particle;
	
	var ambientLight;
	var spotLight;
	var axesHelper;
	//var cameraHelper;

    $(function() {
		stats = initStats();
		scene = new THREE.Scene();
		
		webglRender = new THREE.WebGLRenderer( {antialias: true, alpha: true} ); // antialias anti-aliasing
		webglRender.setSize(window.innerWidth, window.innerHeight);
		webglRender.setClearColor(0x0F0F0F, 1.0); // 0xeeeeee
		webglRender.shadowMap.enabled = true; // Allow Shadow Projection
		render = webglRender;
		
		camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 2147483647); // 2147483647
		camera.position.set(0, 0, 200);
		
		var target = new THREE.Vector3(10, 0 , 0);
		controls = new THREE.OrbitControls(camera, render.domElement);
		controls.target = target;
		camera.lookAt(target);
		
		$('#webgl-output')[0].appendChild(render.domElement);
		window.addEventListener('resize', onWindowResize, false);
		
		// Add a coordinate axis: X (orange), Y (green), Z (blue)
		axesHelper = new THREE.AxesHelper(60);
		scene.add(axesHelper);
		
		ambientLight = new THREE.AmbientLight(0x0c0c0c);
		scene.add(ambientLight);
		
		spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(0, 260, 230);
		spotLight.shadow.mapSize.width = 5120; // Must be a power of 2. The default value is 512.
		spotLight.shadow.mapSize.height = 5120; // Must be a power of 2. The default value is 512.
        spotLight.castShadow = true;
        scene.add(spotLight);
		//cameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
		//scene.add(cameraHelper);
		
		// Create particles
		createParticles4Canvas(true, 0.8, 0xffffff);
		
		/** To save variables that need to be modified */
		guiParams = new function() {
			this.rotationSpeed = 0.01;
		}
		/** Define dat.GUI objects and bind several properties of guiParams */
		var gui = new dat.GUI();
		gui.add(guiParams, 'rotationSpeed', 0, 0.02, 0.001);
		
		renderScene();
    });
	
	/** Render Scene */
	function renderScene() {
		stats.update();
		rotateMesh(); // Rotating object
		
		requestAnimationFrame(renderScene);
		render.render(scene, camera);
	}
	
	/** Initialize stats statistics object */
	function initStats() {
		stats = new Stats();
		stats.setMode(0); // 0 for monitoring FPS; 1 for monitoring rendering time
		$('#stats-output').append(stats.domElement);
		return stats;
	}
	
	/** Triggered when the browser window size changes */
	function onWindowResize() {
		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();
		render.setSize(window.innerWidth, window.innerHeight);
	}
	
	/** Rotating object */
	var step = 0;
	function rotateMesh() {
		step += guiParams.rotationSpeed;
		scene.traverse(function(mesh) {
			if (mesh instanceof THREE.Group) {
				mesh.rotation.x = step;
				//mesh.rotation.y = step;
				//mesh.rotation.z = step;
			}
		});
	}
	
	/** Create 5000 particles */
	function createParticles4Canvas(transparent, opacity, color) {
		var group = new THREE.Group();
		var material = new THREE.SpriteMaterial({
			transparent: transparent,
			opacity, opacity,
			map: getTexture(),
			color: color
		});
		
		var range = 500;
		for (var i = 0; i < 5000; i++) {
			particle = new THREE.Sprite(material);
			var x = Math.random() * range - range / 2;
			var y = Math.random() * range - range / 2;
			var z = Math.random() * range - range / 2;
			particle.position.set(x, y, z);
			group.add(particle);
		}
		
		group.name = "particles";
		scene.remove(scene.getObjectByName(group.name));
		scene.add(group);
	}
	
	/** Using Canvas to create a fairy-like texture pattern */
	function getTexture() {
		var canvas = document.createElement('canvas');
		canvas.width = 32;
		canvas.height = 32;
		var ctx = canvas.getContext('2d');
		
		ctx.translate(-81, -84);

		ctx.fillStyle = "orange";
		ctx.beginPath();
		ctx.moveTo(83, 116);
		ctx.lineTo(83, 102);
		ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
		ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
		ctx.lineTo(111, 116);
		ctx.lineTo(106.333, 111.333);
		ctx.lineTo(101.666, 116);
		ctx.lineTo(97, 111.333);
		ctx.lineTo(92.333, 116);
		ctx.lineTo(87.666, 111.333);
		ctx.lineTo(83, 116);
		ctx.fill();

		// Eye
		ctx.fillStyle = "white";
		ctx.beginPath();
		ctx.moveTo(91, 96);
		ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
		ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
		ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
		ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
		ctx.moveTo(103, 96);
		ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
		ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
		ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
		ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
		ctx.fill();

		// pupil
		ctx.fillStyle = "blue";
		ctx.beginPath();
		ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
		ctx.fill();
		ctx.beginPath();
		ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
		ctx.fill();
		
		var texture = new THREE.Texture(canvas);
		texture.needsUpdate = true;
		return texture;
	}
	
</script>
</body>
</html>
In this example, the process of creating particles is basically the same as in the previous article. The only difference is that when we construct THREE.SpriteMaterial material, we pass in several specific parameters, in which the map parameter passes in a getTexture() function. In this function, we first create a Canvas object with document.createElement('canvas') and set its width and height to 32. Then get its two-dimensional canvas through the getContext('2d') function, and then draw a fairy pattern using some of its drawing API s. Finally, we use THREE.Texture() constructs a texture object and returns it to the caller. The final rendering looks like this.

Therefore, the advantage of this method is that you can format the appearance of your particles as you like. But this requires you to be very proficient in the various uses of the Canvas API.

To be continued uuuuuuuuuuuuu

Posted by markjohnson on Sat, 05 Jan 2019 17:54:09 -0800