Closed3
Pyhtonウェブアプリを作る:Three.js の復習

今回のスクラップはフロントエンドの実装部分でThree.jsを使いたいのでその復習.
やりたいこと
WebGLをjavascriptで簡単に扱えるライブラリThree.jsを復習する.
前提
Pythonのウェブアプリ開発フレームワークFlaskを使っている.
Three.js のCDNを使ってみる
以前はnpmを使っていたが今回は簡単に使いたいのでHTMLに数行書き込むだけで使えるCDNを使わせてもらう.
layout.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<script type="importmap">
{
"imports": {
"Three": "https://cdn.skypack.dev/three@0.128.0",
"OrbitContoles": "https://cdn.skypack.dev/three@0.128.0/examples/jsm/controls/OrbitControls.js",
"DragContoles": "https://cdn.skypack.dev/three@0.128.0/examples/jsm/controls/DragControls.js",
"BufferGeometryUtils": "https://cdn.skypack.dev/three@0.128.0/examples/jsm/utils/BufferGeometryUtils.js"
}
}
</script>
<script type="module" src="{{ url_for('static', filename='js/init.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/stylesheet.css') }}">
<title>{{ title }}</title>
{% block head%}{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>
index.html
{% extends "layout.html" %}
{% block content %}
<h1>vis-g</h1>
{{ name }}
<body>
<canvas id="canvas"></canvas>
</body>
{% endblock %}
init.js
import * as THREE from 'Three';
import { OrbitControls } from 'OrbitContoles';
import { BufferGeometryUtils } from 'BufferGeometryUtils';
export let renderer, scene, camera, controls;
export let div = document.querySelector("#canvas");
export let buttons = {};
window.addEventListener('DOMContentLoaded', init);
function init() {
const canvasElement = div.querySelector("canvas");
const width = canvasElement.offsetWidth;
const height = canvasElement.offsetHeight;
// レンダラーを作成
renderer = new THREE.WebGLRenderer({
canvas: canvasElement,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xffffff, 1.0);
renderer.setSize(width, height);
// シーンを作成
scene = new THREE.Scene();
// カメラを作成
camera = new THREE.PerspectiveCamera(
45,
width / height,
1,
100
);
camera.position.set(25, 25, 25);
// XYZ軸
setCoordinate(10); // length: 10
// カメラコントローラーを作成
controls = new OrbitControls(camera, renderer.domElement);
//
controls.maxPolarAngle = Math.PI * 0.5;
controls.minDistance = 0.1;
controls.maxDistance = 100;
// カメラの自動回転設定
// 滑らかにカメラコントローラーを制御する
controls.enableDamping = true;
controls.dampingFactor = 0.2;
controls.enabled = true;
buttons['OrbitControls'] = makeSwitchButton(div, 'oc', 'ぐりぐり', controls);
// 平行光源
const directionalLight = new THREE.DirectionalLight(
0xffffff
);
directionalLight.position.set(1, 1, 1);
// シーンに追加
scene.add(directionalLight);
// 初回実行
renderer.render(scene, camera);
function setCoordinate(h) { // {{{
const r = 0.1;
const origin = [0, 0, 0];
const directions = [ [h, 0, 0], [0, h, 0], [0, 0, h]];
const rotations = [ [0, 0, -h], [0, h, 0], [h, 0, 0]];
const colors = [new THREE.Color("#ff0000"), new THREE.Color("#00ff00"), new THREE.Color("#0000ff")];
for (let i = 0; i < 3; i++) {
const axes = [];
const material = new THREE.MeshPhongMaterial({color: colors[i]});
const axis = new THREE.Vector3(rotations[i][0],rotations[i][1],rotations[i][2]).normalize();
const q = new THREE.Quaternion();
q.setFromAxisAngle(axis, 0.5*Math.PI);
const geometryArrow = makeArrowGeometry(r, h);
geometryArrow.applyMatrix4(new THREE.Matrix4().makeRotationFromQuaternion(q));;
const mesh = new THREE.Mesh(geometryArrow, material);
scene.add(mesh);
}; // }}}
};
};
function makeArrowGeometry(r, h) {
const axes = [];
const n = 10;
const geometryCylinder = new THREE.CylinderGeometry(r, r, h*0.8, 20, n*h, false);
geometryCylinder.translate(0, h*0.4, 0);
axes.push(geometryCylinder);
const geometryCone = new THREE.CylinderGeometry(0, r*5, h*0.2, 20, n, false);
geometryCone.translate(0, h*0.9, 0);
axes.push(geometryCone);
return BufferGeometryUtils.mergeBufferGeometries(axes);
};
export function makeSwitchButton(div, id, str, obj) {
let html = document.createElement('input');
html.type = "button";
html.value = str + ": " + ((obj.enabled) ? "on" : "off");
html.addEventListener('click', function(e){
obj.enabled = !obj.enabled;
html.value = str + ": " + ((obj.enabled) ? "on" : "off");
});
div.appendChild(html);
return html;
};
index.js
import * as THREE from 'Three';
import { DragControls } from 'DragContoles';
import { renderer, scene, camera, controls, div, buttons, makeSwitchButton } from './init.js';
window.addEventListener('DOMContentLoaded', index);
function index() {
// let n = 2;
// const cube = new THREE.Mesh(
// new THREE.BoxGeometry(n, n, n),
// new THREE.MeshNormalMaterial());
// cube.position.set(n*0.5,n*0.5,n*0.5);
// scene.add(cube);
const geometry = new THREE.BoxGeometry()
const material = [
new THREE.MeshPhongMaterial({ color: 0xff0000, transparent: true }),
new THREE.MeshPhongMaterial({ color: 0x00ff00, transparent: true }),
new THREE.MeshPhongMaterial({ color: 0x0000ff, transparent: true })
];
const cubes = [
new THREE.Mesh(geometry, material[0]),
new THREE.Mesh(geometry, material[1]),
new THREE.Mesh(geometry, material[2])
];
const cubes2 = [
new THREE.Mesh(geometry, material[0]),
new THREE.Mesh(geometry, material[1]),
new THREE.Mesh(geometry, material[2])
];
cubes[0].position.x = -2;
cubes[1].position.x = 0;
cubes[2].position.x = 2;
cubes.forEach((c) => scene.add(c));
cubes2.forEach((c) => scene.add(c));
var dragcontrols = new DragControls(cubes, camera, renderer.domElement);
buttons['DragControls'] = makeSwitchButton(div, 'dc', 'うごかす', dragcontrols);
update();
function render() {
// cube.position.x += 0.01;
// cube.rotation.x += 0.01;
cubes2[0].position.x = -cubes[0].position.x;
cubes2[0].position.y = -cubes[0].position.y;
cubes2[0].position.z = -cubes[0].position.z;
renderer.render(scene, camera);
};
function update() {
// レンダリング
render();
requestAnimationFrame(update);
};
};
参考:
このスクラップは2022/07/31にクローズされました