View Icon ۶۱

ساخت تاس چند وجهی با لایبرری تری جی اس Make Dice with Three.js

... Loading
Post Pic

این کد با استفاده از رابط WebGL و لایبرری Three.js سه نوع تاس مکعبی و چند وجهی روی صفحه ایجاد میکند که سه بعدی هستند و امکان چرخش از زاویه های مختلف با موس دارند و همچنین دارای نور پردازی جالبی نیز هست. شماره کد: 8

منبع کد
HTML
CSS
body{
overflow: hidden;
margin: 0;
}
JS
import * as THREE from "three";
import {OrbitControls} from "three/addons/controls/OrbitControls.js";
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
console.clear();
// load fonts
await (async function () {
async function loadFont(fontface) {
await fontface.load();
document.fonts.add(fontface);
}
let fonts = [
new FontFace(
"Calistoga",
"url(https://fonts.gstatic.com/s/calistoga/v15/6NUU8F2OJg6MeR7l4e0fs8wB.woff2) format('woff2')"
)
];
for (let font in fonts) {
//console.log(fonts[font]);
await loadFont(fonts[font]);
}
})();
class DiceCube extends THREE.Mesh{
constructor(){
let tileDimension = new THREE.Vector2(4, 2);
let tileSize = 512;
let a = 2 / Math.sqrt(3);
let g = new THREE.BoxGeometry(a, a, a);
let corner = 0.25;
let radius = 0.1;
let dots = [
[[0, 0]],
[[-1, -1], [1, 1]],
[[-1, -1], [0, 0], [1, 1]],
[[-1, -1], [-1, 1], [1, 1], [1, -1]],
[[-1, -1], [-1, 1], [1, 1], [1, -1], [0, 0]],
[[-1, -1], [-1, 0], [-1, 1], [1, 1], [1, 0], [1, -1]]
].map(d => {
return Array.from({length: d.length}, (p, idx) => {return new THREE.Vector2(...d[idx])});
})
console.log(dots);
let numbers = [0, 5, 1, 4, 2, 3];
let c = document.createElement("canvas");
c.width = tileSize * tileDimension.x;
c.height = tileSize * tileDimension.y;
console.log(c.width, c.height);
let ctx = c.getContext("2d");
ctx.fillStyle = "#008";
ctx.fillRect(0, 0, c.width, c.height)
let baseUVs = [
[0, 1],
[1, 1],
[0, 0],
[1, 0]
].map(p => {return new THREE.Vector2(...p)});
let uvs = [];
let vTemp = new THREE.Vector2();
let vCenter = new THREE.Vector2(0.5, 0.5);
for(let i = 0; i < 6; i++){
let u = i % tileDimension.x;
let v = Math.floor(i / tileDimension.x);
baseUVs.forEach(buv => {
uvs.push((buv.x + u) / tileDimension.x, (buv.y + v) / tileDimension.y);
})
ctx.fillStyle = "aqua";
ctx.beginPath();
dots[numbers[i]].forEach(d => {
vTemp.copy(d).multiplyScalar(corner).add(vCenter);
vTemp.x += u;
vTemp.y += (tileDimension.y - 1) - v;
vTemp.multiplyScalar(tileSize);
ctx.moveTo(vTemp.x + tileSize * radius, vTemp.y);
ctx.arc(vTemp.x, vTemp.y, tileSize * radius, 0, Math.PI * 2);
});
ctx.fill();
}
g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let tex = new THREE.CanvasTexture(c);
tex.colorSpace = THREE.SRGBColorSpace;
let m = new THREE.MeshStandardMaterial({
map: tex,
metalness: 0.75,
roughness: 0.25
});
super(g, m);
}
}
class DiceDodecahedron extends THREE.Mesh{
constructor(){
let tileDimension = new THREE.Vector2(4, 3);
let tileSize = 512;
let g = new THREE.DodecahedronGeometry();
let c = document.createElement("canvas");
c.width = tileSize * tileDimension.x;
c.height = tileSize * tileDimension.y;
let ctx = c.getContext("2d");
ctx.fillStyle = "#040";
ctx.fillRect(0, 0, c.width, c.height);
let uvs = [];
const base = new THREE.Vector2(0, 0.5);
const center = new THREE.Vector2();
const angle = THREE.MathUtils.degToRad(72);
let baseUVs = [
base.clone().rotateAround(center, angle * 1).addScalar(0.5),
base.clone().rotateAround(center, angle * 2).addScalar(0.5),
base.clone().rotateAround(center, angle * 3).addScalar(0.5),
base.clone().rotateAround(center, angle * 4).addScalar(0.5),
base.clone().rotateAround(center, angle * 0).addScalar(0.5)
];
for(let i = 0; i < 12; i++){
let u = i % tileDimension.x;
let v = Math.floor(i / tileDimension.x);
uvs.push(
(baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y,
(baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y,
(baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y,
(baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y,
(baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y,
(baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y,
(baseUVs[3].x + u) / tileDimension.x, (baseUVs[3].y + v) / tileDimension.y,
(baseUVs[4].x + u) / tileDimension.x, (baseUVs[4].y + v) / tileDimension.y,
(baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = `bold ${tileSize / 3}px Calistoga`;
ctx.fillStyle = "#0f0";
ctx.fillText((i + 1) + ((i == 5 || i == 8) ? "." : ""), (u + 0.5) * tileSize, c.height - ((v + 0.5) * tileSize));
}
g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let tex = new THREE.CanvasTexture(c);
tex.colorSpace = THREE.SRGBColorSpace;
let m = new THREE.MeshStandardMaterial({
map: tex,
metalness: 0.75,
roughness: 0.25
});
super(g, m);
}
}
class DiceIcosahedron extends THREE.Mesh{
constructor(){
let tileDimension = new THREE.Vector2(4, 5);
let tileSize = 512;
let g = new THREE.IcosahedronGeometry();
let c = document.createElement("canvas");
c.width = tileSize * tileDimension.x;
c.height = tileSize * tileDimension.y;
let ctx = c.getContext("2d");
ctx.fillStyle = "#400";
ctx.fillRect(0, 0, c.width, c.height);
let uvs = [];
let baseUVs = [
[0.067, 0.25],
[0.933, 0.25],
[0.5, 1]
].map((p) => {return new THREE.Vector2(...p)});
for(let i = 0; i < 20; i++){
let u = i % tileDimension.x;
let v = Math.floor(i / tileDimension.x);
uvs.push(
(baseUVs[0].x + u) / tileDimension.x, (baseUVs[0].y + v) / tileDimension.y,
(baseUVs[1].x + u) / tileDimension.x, (baseUVs[1].y + v) / tileDimension.y,
(baseUVs[2].x + u) / tileDimension.x, (baseUVs[2].y + v) / tileDimension.y
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = `bold ${tileSize / 3}px Calistoga`;
ctx.fillStyle = "orange";
ctx.fillText((i + 1) + ((i == 5 || i == 8) ? "." : ""), (u + 0.5) * tileSize, c.height - ((v + 0.5) * tileSize));
}
g.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
let tex = new THREE.CanvasTexture(c);
tex.colorSpace = THREE.SRGBColorSpace;
let m = new THREE.MeshStandardMaterial({
map: tex,
metalness: 0.75,
roughness: 0.25
});
super(g, m);
}
}
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(5, 3, 3);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
let pmremGenerator = new THREE.PMREMGenerator( renderer );
let envTex = pmremGenerator.fromScene( new RoomEnvironment(), 0.04 ).texture;
scene.background = new THREE.Color( 0x444444 );
scene.environment = envTex;
let light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.set(1, 3, 5);
scene.add(light, new THREE.AmbientLight(0xffffff, Math.PI * 0.5));
let dice = [];
let diceIco = new DiceIcosahedron();
dice.push(diceIco)
scene.add(diceIco);
let diceDod = new DiceDodecahedron();
dice.push(diceDod);
scene.add(diceDod);
let diceCube = new DiceCube();
dice.push(diceCube)
scene.add(diceCube);
dice.forEach((d, idx) => {
let a = Math.PI * 2 / 3 * idx;
d.position.set(0, 0, -1.25).applyAxisAngle(new THREE.Vector3(0, 1, 0), a);
d.rotation.y = a;
})
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
renderer.setAnimationLoop(() => {
controls.update();
renderer.render(scene, camera);
})
HTML
8
CSS
4
JS
220

CM
(۰)
Copy link