Skip to content

Instantly share code, notes, and snippets.

@YeYongFen
Created February 1, 2023 09:04
Show Gist options
  • Save YeYongFen/245931993c3dbd5fd0084d6c634430d5 to your computer and use it in GitHub Desktop.
Save YeYongFen/245931993c3dbd5fd0084d6c634430d5 to your computer and use it in GitHub Desktop.
WebGL Annotations (three.js)
<div class="annotation">
<p><strong>Cube</strong></p>
<p>In geometry, a cube is a three-dimensional solid object bounded by six square faces, facets or sides, with three meeting at each vertex.</p>
</div>
<canvas id="number" width="64" height="64"></canvas>
// Number
const canvas = document.getElementById("number");
const ctx = canvas.getContext("2d");
const x = 32;
const y = 32;
const radius = 30;
const startAngle = 0;
const endAngle = Math.PI * 2;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.fill();
ctx.strokeStyle = "rgb(255, 255, 255)";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.stroke();
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.font = "32px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("1", x, y);
// three.js
let camera;
let controls;
let scene;
let renderer;
let sprite;
let mesh;
let spriteBehindObject;
const annotation = document.querySelector(".annotation");
init();
animate();
function init() {
// Camera
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 2, 2000);
camera.position.x = 750;
camera.position.y = 500;
camera.position.z = 1250;
// Scene
scene = new THREE.Scene();
// Lights
const lights = [];
lights[0] = new THREE.PointLight(0xffffff, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = new THREE.PointLight(0xffffff, 1, 0);
lights[0].position.set(0, 2000, 0);
lights[1].position.set(1000, 2000, 1000);
lights[2].position.set(-1000, -2000, -1000);
scene.add(lights[0]);
scene.add(lights[1]);
scene.add(lights[2]);
// Mesh
const cubeGeometry = new THREE.BoxGeometry(500, 500, 500);
mesh = new THREE.Mesh(
cubeGeometry,
new THREE.MeshPhongMaterial({
color: 0x156289,
emissive: 0x072534,
side: THREE.DoubleSide,
shading: THREE.FlatShading
})
);
const line = new THREE.LineSegments(
new THREE.WireframeGeometry(cubeGeometry),
new THREE.LineBasicMaterial({
color: 0xffffff,
linewidth: 1,
opacity: 0.25,
transparent: true
})
);
scene.add(mesh);
scene.add(line);
// Sprite
const numberTexture = new THREE.CanvasTexture(
document.querySelector("#number")
);
const spriteMaterial = new THREE.SpriteMaterial({
map: numberTexture,
alphaTest: 0.5,
transparent: true,
depthTest: false,
depthWrite: false
});
sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(250, 250, 250);
sprite.scale.set(60, 60, 1);
scene.add(sprite);
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x333333, 1);
document.body.appendChild(renderer.domElement);
// Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableZoom = 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);
controls.update();
render();
}
function render() {
renderer.render(scene, camera);
updateAnnotationOpacity();
updateScreenPosition();
}
function updateAnnotationOpacity() {
const meshDistance = camera.position.distanceTo(mesh.position);
const spriteDistance = camera.position.distanceTo(sprite.position);
spriteBehindObject = spriteDistance > meshDistance;
sprite.material.opacity = spriteBehindObject ? 0.25 : 1;
// Do you want a number that changes size according to its position?
// Comment out the following line and the `::before` pseudo-element.
sprite.material.opacity = 0;
}
function updateScreenPosition() {
const vector = new THREE.Vector3(250, 250, 250);
const canvas = renderer.domElement;
vector.project(camera);
vector.x = Math.round((0.5 + vector.x / 2) * (canvas.width / window.devicePixelRatio));
vector.y = Math.round((0.5 - vector.y / 2) * (canvas.height / window.devicePixelRatio));
annotation.style.top = `${vector.y}px`;
annotation.style.left = `${vector.x}px`;
annotation.style.opacity = spriteBehindObject ? 0.25 : 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
<script src="https://codepen.io/Lorti/pen/mNzxjE.js"></script>
canvas {
width: 100%;
height: 100px;
display: block;
}
.annotation {
position: absolute;
top: 0;
left: 0;
z-index: 1;
margin-left: 15px;
margin-top: 15px;
padding: 1em;
width: 200px;
color: #fff;
background: rgba(0, 0, 0, 0.8);
border-radius: .5em;
font-size: 12px;
line-height: 1.2;
transition: opacity .5s;
&::before {
content: '1';
position: absolute;
top: -30px;
left: -30px;
width: 30px;
height: 30px;
border: 2px solid #fff;
border-radius: 50%;
font-size: 16px;
line-height: 30px;
text-align: center;
background: rgba(0, 0, 0, 0.8);
}
}
#number {
position: absolute;
z-index: -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment