Three.js Emoji
by Matt
HTML
<base href="https://rawcdn.githack.com/mrdoob/three.js/r163/examples/" />
<script async src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@^0.163.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@^0.163.0/examples/jsm/",
"lil-gui": "https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.esm.min.js"
}
}
</script>
CSS
canvas {
position: fixed;
inset: 0;
}
JavaScript
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'lil-gui';
let camera, scene, renderer, stats;
let mesh;
const amount = parseInt( window.location.search.slice( 1 ) ) || 10;
const count = Math.pow( amount, 3 );
const dummy = new THREE.Object3D();
init();
animate();
function characterToDataURL(text, size = 64) {
const canvas = document.createElement("canvas")
canvas.width = size
canvas.height = size
const ctx = canvas.getContext("2d")
ctx.font = `${size}px sans-serif`
const {
actualBoundingBoxRight,
actualBoundingBoxLeft,
actualBoundingBoxDescent,
actualBoundingBoxAscent,
} = ctx.measureText(text)
const centerX = 0.5 * (actualBoundingBoxRight - actualBoundingBoxLeft)
const centerY = 0.5 * (actualBoundingBoxDescent - actualBoundingBoxAscent)
ctx.fillText(text, size / 2 - centerX, size / 2 - centerY)
return canvas.toDataURL()
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( amount * 0.9, amount * 0.9, amount * 0.9 );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
const texture = loader.load(characterToDataURL("💩"))
texture.colorSpace = THREE.SRGBColorSpace
const geometry = new THREE.PlaneGeometry()
const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide, alphaTest: 0.5 });
mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );
//
const gui = new GUI();
gui.add( mesh, 'count', 0, count );
//
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
stats = new Stats();
...