4
3

3D空間からの脱出ゲーム。

Last updated at Posted at 2024-09-13

3D空間に入ってしまうゲーム。webカメラでリアルタイム描画。

image.png

スクリーンショット 2024-09-13 212111.png

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D空間に人物画像を描画</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
        #video {
            display: none; /* カメラの映像は隠しますが、内部で使用します */
        }
    </style>
</head>
<body>
    <video id="video" width="640" height="480" autoplay></video>
    <canvas id="output-canvas" style="display:none;"></canvas>

    <script>
        let scene, camera, renderer;
        let personTexture;
        let net;

        // Three.jsのシーンを初期化
        function initThreeJS() {
            scene = new THREE.Scene();

            // 背景を緑色に設定
            scene.background = new THREE.Color(0x00ff00);

            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.z = 5;

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            // ウィンドウリサイズ時にレンダラーとカメラのアスペクト比を調整
            window.addEventListener('resize', () => {
                const width = window.innerWidth;
                const height = window.innerHeight;
                renderer.setSize(width, height);
                camera.aspect = width / height;
                camera.updateProjectionMatrix();
            });

            // カラフルなボックスを10個作成
            const geometry = new THREE.BoxGeometry(1, 1, 1);
            for (let i = 0; i < 10; i++) {
                const material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });
                const cube = new THREE.Mesh(geometry, material);
                cube.position.set((Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10, (Math.random() - 0.5) * 10);
                scene.add(cube);
            }

            animate();
        }

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);
            scene.rotation.x += 0.001;
            scene.rotation.y += 0.001;
            renderer.render(scene, camera);
        }

        // 人物の画像を平面に描画
        function addPersonToScene(texture) {
            const geometry = new THREE.PlaneGeometry(3, 4);
            const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
            const plane = new THREE.Mesh(geometry, material);
            scene.add(plane);
        }

        // BodyPixのモデルをロード
        async function loadBodyPix() {
            net = await bodyPix.load();
            console.log('BodyPixモデルがロードされました');
        }
        loadBodyPix();

        // Webカメラの映像を取得
        async function setupCamera() {
            const video = document.getElementById('video');
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            video.srcObject = stream;
            return new Promise(resolve => {
                video.onloadedmetadata = () => {
                    resolve();
                };
            });
        }

        // 画像を処理して人物のみを抽出し、背景を緑にする
        async function processImage() {
            const video = document.getElementById('video');
            const outputCanvas = document.getElementById('output-canvas');
            const ctx = outputCanvas.getContext('2d');

            outputCanvas.width = video.videoWidth;
            outputCanvas.height = video.videoHeight;

            ctx.drawImage(video, 0, 0);

            // セグメンテーションを実行
            const segmentation = await net.segmentPerson(outputCanvas);

            // 画像のピクセルデータを取得
            const imageData = ctx.getImageData(0, 0, outputCanvas.width, outputCanvas.height);
            const data = imageData.data;

            // セグメンテーション結果に基づいて人物部分を描画し、背景を緑色に設定
            for (let i = 0; i < data.length; i += 4) {
                const n = i / 4;
                if (segmentation.data[n] === 0) {
                    // 背景部分は緑色に設定 (RGB: 0, 255, 0)
                    data[i] = 0;      // R
                    data[i + 1] = 255; // G
                    data[i + 2] = 0;   // B
                    data[i + 3] = 255; // アルファ値(透明度)
                }
            }
            ctx.putImageData(imageData, 0, 0);

            // Three.js用のテクスチャとして設定
            personTexture = new THREE.Texture(outputCanvas);
            personTexture.needsUpdate = true;

            // 人物をシーンに追加
            addPersonToScene(personTexture);
        }

        // リアルタイム処理を実行
        async function renderLoop() {
            await processImage();
            requestAnimationFrame(renderLoop); // 次のフレームを処理
        }

        // Three.jsの初期化とカメラのセットアップ
        async function init() {
            await setupCamera();
            initThreeJS();
            renderLoop(); // リアルタイム処理開始
        }
        init();
    </script>
</body>
</html>

Webカメラから顔追跡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Real-time Face Tracking with TensorFlow.js</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface"></script>
    <style>
        video, canvas {
            display: block;
            margin: 20px auto;
            max-width: 100%;
        }
    </style>
</head>
<body>
    <h1>Real-time Face Tracking with TensorFlow.js</h1>

    <!-- Webカメラの映像を表示する -->
    <video id="webcam" autoplay playsinline width="640" height="480"></video>

    <!-- 顔トラッキング結果を表示するキャンバス -->
    <canvas id="faceCanvas" width="640" height="480"></canvas>

    <script>
        let model;
        const video = document.getElementById('webcam');
        const canvas = document.getElementById('faceCanvas');
        const ctx = canvas.getContext('2d');

        // BlazeFaceモデルをロードする関数
        async function loadModel() {
            model = await blazeface.load();
            console.log("BlazeFace model loaded.");
            startFaceTracking();
        }

        // Webカメラ映像を取得する関数
        async function setupWebcam() {
            return new Promise((resolve, reject) => {
                const constraints = {
                    video: true
                };
                navigator.mediaDevices.getUserMedia(constraints)
                    .then((stream) => {
                        video.srcObject = stream;
                        video.onloadedmetadata = () => {
                            resolve(video);
                        };
                    })
                    .catch((err) => {
                        reject(err);
                    });
            });
        }

        // 顔をリアルタイムで追跡する関数
        async function startFaceTracking() {
            await setupWebcam();
            video.play();

            // リアルタイムで顔を検出し、キャンバスに描画するループ
            const detectFaces = async () => {
                const returnTensors = false;
                const predictions = await model.estimateFaces(video, returnTensors);

                // キャンバスをクリアして新たに描画
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

                if (predictions.length > 0) {
                    predictions.forEach(prediction => {
                        const [startX, startY] = prediction.topLeft;
                        const [endX, endY] = prediction.bottomRight;
                        const width = endX - startX;
                        const height = endY - startY;

                        // 顔の四角形を描画
                        ctx.beginPath();
                        ctx.rect(startX, startY, width, height);
                        ctx.lineWidth = 2;
                        ctx.strokeStyle = 'red';
                        ctx.stroke();

                        // ランドマーク(目や鼻など)の描画
                        prediction.landmarks.forEach(landmark => {
                            ctx.beginPath();
                            ctx.arc(landmark[0], landmark[1], 5, 0, 2 * Math.PI);
                            ctx.fillStyle = 'blue';
                            ctx.fill();
                        });
                    });
                }
                requestAnimationFrame(detectFaces);
            };
            detectFaces();
        }

        // モデルをロードし、Webカメラをセットアップしてから顔追跡を開始
        loadModel();
    </script>
</body>
</html>

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3