<!DOCTYPE html> <html lang="en"> <head> <title>three.js webgl - vector - text</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { font-family: Monospace; background-color: #f0f0f0; margin: 0px; overflow: hidden; } #info { position: absolute; top: 10px; width: 100%; text-align: center; } </style> </head> <body> <div id="info"> <a href="http://threejs.org" target="_blank">three.js</a> webgl - Resolution-Independent Vector Fonts. <a href="https://github.com/mrdoob/three.js/issues/4746">info</a>. </div> <script src="../build/three.min.js"></script> <script src="./js/controls/OrbitControls.js"></script> <script src="js/libs/stats.min.js"></script> <!-- load the font file from canvas-text --> <script src="fonts/helvetiker_regular.typeface.js"></script> <script type="x-shader/x-fragment" id="fs"> varying vec2 vUv; varying float flip; uniform vec3 color; float inCurve(vec2 uv) { return uv.x * uv.x - uv.y; } float delta = 0.1; void main() { float x = inCurve(vUv); if (x * flip > 0.) discard; gl_FragColor = vec4(color, 1.); } </script> <script type="x-shader/x-vertex" id="vs"> varying vec2 vUv; attribute float invert; varying float flip; void main() { vUv = uv; flip = invert; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; } </script> <script> var stats; var camera, scene, renderer, controls; var group, text; var t = false; function toggle() { if ( t ) { text2.visible = 0; text1.visible = 1; } else { text2.visible = 1; text1.visible = 0; } t = !t; } init(); animate(); function init() { camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.set( 0, 100, 500 ); controls = new THREE.OrbitControls( camera ); controls.center.set( 0, 100, 0 ); scene = new THREE.Scene(); var theText = "&"; // i % & j b 8 var options = { size: 180, height: 20, curveSegments: 2, font: "helvetiker", bevelEnabled: false }; group = new THREE.Group(); scene.add( group ); var textMaterial = new THREE.MeshBasicMaterial( { color: new THREE.Color(0, 0, 1 ), overdraw: 0.5, wireframe: true, side: THREE.DoubleSide } ); textShapes = THREE.FontUtils.generateShapes( theText, options ); text3d = new THREE.ShapeGeometry( textShapes ); text3d.computeBoundingBox(); var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x ); text = new THREE.Mesh( text3d, textMaterial ); text.position.x = centerOffset - 150; group.add( text ); // vA = new THREE.Vector2(); vB = new THREE.Vector2(); vDot = new THREE.Vector2(); function processShape(path, reverse) { var pts = []; // bigger area (convex hull) var pts2 = []; // smaller area (full solid shapes) var beziers = []; // quad bezier points var invert = []; var z; var wind; pts.push( path[0].getPoint(0) ); pts2.push( path[0].getPoint(0) ); for (var i=0; i < path.length; i++) { curve = path[i]; if (curve instanceof THREE.LineCurve) { pts.push( curve.v2 ); pts2.push( curve.v2 ); } else if (curve instanceof THREE.QuadraticBezierCurve) { vA = vA.subVectors( curve.v1, curve.v0 ); // .normalize() vB = vB.subVectors( curve.v2, curve.v1 ); z = vA.x * vB.y - vA.y * vB.x; // z component of cross Production wind = z < 0; // clockwise/anticlock wind // if (reverse) wind = !wind; // console.log(z, wind , wind ? 'clockwise' : 'anti'); if (wind) { pts.push( curve.v1 ); pts.push( curve.v2 ); pts2.push( curve.v2 ); } else { pts.push( curve.v2 ); pts2.push( curve.v1 ); pts2.push( curve.v2 ); } var flip = wind ? 1 : -1; // if (reverse) flip *= -1; invert.push(flip, flip, flip); beziers.push( curve.v0, curve.v1, curve.v2); } } return { pts: pts, pts2: pts2, beziers: beziers, invert: invert }; } var subshape; var convexhullShapeGroup = []; var solidShapeGroup = []; var beziers = [], invert = []; for (var s=0;s<textShapes.length;s++) { subshape = textShapes[s]; var process = processShape(subshape.curves); pts = process.pts; pts2 = process.pts2; beziers = beziers.concat(process.beziers); invert = invert.concat(process.invert); convexhullShape = new THREE.Shape( pts ); solidShape = new THREE.Shape( pts2 ); convexhullShapeGroup.push( convexhullShape ); solidShapeGroup.push( solidShape ); for (var i=0; i<subshape.holes.length;i++) { hole = subshape.holes[i]; // console.log('hole', hole); process = processShape(hole.curves, true); pts = process.pts; pts2 = process.pts2; beziers = beziers.concat(process.beziers); invert = invert.concat(process.invert); convexhullShape.holes.push(new THREE.Shape(pts)); solidShape.holes.push(new THREE.Shape(pts2)); } } // end of subshape bezierGeometry = new THREE.Geometry(); for (var i=0;i<beziers.length;i++) { p = beziers[i]; bezierGeometry.vertices.push( new THREE.Vector3(p.x, p.y, 0) ); } for (i=0;i<beziers.length;i+=3) { bezierGeometry.faces.push( new THREE.Face3(i, i+1, i+2) ); bezierGeometry.faceVertexUvs[0].push( [ new THREE.Vector2(0, 0), new THREE.Vector2(0.5, 0), new THREE.Vector2(1, 1) ] ); } text3d = new THREE.ShapeGeometry( convexhullShapeGroup ); text3d.computeBoundingBox(); var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x ); text1 = new THREE.Mesh( text3d, textMaterial ); text1.position.x = centerOffset + 150; group.add( text1 ); text3d = new THREE.ShapeGeometry( solidShapeGroup ); text3d.computeBoundingBox(); var centerOffset = -0.5 * ( text3d.boundingBox.max.x - text3d.boundingBox.min.x ); text2 = new THREE.Mesh( text3d, new THREE.MeshBasicMaterial( { color: new THREE.Color(1, 0, 0 ), side: THREE.DoubleSide, wireframe: true } ) ); text2.position.x = centerOffset + 150; group.add( text2 ); // bezierGeometry.computeBoundingBox(); bezierGeometry.computeFaceNormals(); bezierGeometry.computeVertexNormals(); // var uniforms = { color: { type: 'c', value: new THREE.Color(0.45 * 0xffffff) } }; var vertexShader = document.getElementById( 'vs' ).textContent; var fragmentShader = document.getElementById( 'fs' ).textContent; newMaterial = new THREE.ShaderMaterial({ attributes: { invert: { type: 'f', value: invert } }, uniforms: uniforms, vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide }); text = new THREE.Mesh( bezierGeometry, newMaterial ); text.position.x = centerOffset; text.position.y = 0; text.position.z = 0; text.rotation.x = 0; text.rotation.y = Math.PI * 2; group.add( text ); // text3d = new THREE.ShapeGeometry( solidShapeGroup ); text3d.computeBoundingBox(); text = new THREE.Mesh( text3d, new THREE.MeshBasicMaterial( { color: 0.45 * 0xffffff, side: THREE.DoubleSide } ) ); text.position.x = centerOffset; text.position.y = 0; text.position.z = 0; text.rotation.x = 0; text.rotation.y = Math.PI * 2; group.add( text ); // renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setClearColor( 0xf0f0f0 ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; document.body.appendChild( stats.domElement ); document.addEventListener( 'mousedown', toggle, false ); window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } // function animate() { requestAnimationFrame( animate ); render(); stats.update(); } function render() { controls.update(); renderer.render( scene, camera ); } </script> </body> </html>