未命名 6U2MWUedit icon

创建者:
用户tnhBzOUO
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            font-family: Arial, sans-serif;
            background: #000;
        }

        canvas {
            display: block;
            cursor: grab;
        }

        canvas:active {
            cursor: grabbing;
        }

        .info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            background: rgba(255, 0, 0, 0.8);
            padding: 15px;
            border-radius: 8px;
            z-index: 100;
            font-size: 14px;
            line-height: 1.4;
            border: 2px solid #ff4444;
        }

        .warning {
            color: #ffff00;
            font-weight: bold;
            text-shadow: 0 0 5px rgba(255, 255, 0, 0.5);
        }

        .performance-indicator {
            position: absolute;
            bottom: 10px;
            left: 10px;
            color: white;
            padding: 10px;
            border-radius: 5px;
            z-index: 100;
            font-family: monospace;
            border: 2px solid #ff4444;
        }

        .draw-calls {
            color: #ff4444;
            font-weight: bold;
            font-size: 16px;
        }
    </style>
</head>

<body>
    <div class="performance-indicator">
        <div id="fps">FPS: 0</div>
        <div id="draw-calls" class="draw-calls">Draw Calls: 0</div>
    </div>

    <script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
    <script>
        const CONFIG = {
            TOTAL_LINES: 1500000,       // 总线段数:150万条
            LINES_PER_GROUP: 100,       // 每组线段数:100条

            get MAX_TRANSFORMS() {
                return Math.ceil(this.TOTAL_LINES / this.LINES_PER_GROUP);
            },

            get GRID_SIZE() {
                const totalGroups = this.MAX_TRANSFORMS;
                const cubeRoot = Math.ceil(Math.pow(totalGroups, 1 / 3));
                let x = cubeRoot;
                let y = cubeRoot;
                let z = Math.ceil(totalGroups / (x * y));

                while (x * y * z < totalGroups) {
                    if (x <= y) x++;
                    else y++;
                    z = Math.ceil(totalGroups / (x * y));
                }

                return { X: x, Y: y, Z: z };
            },

            LINE_LENGTH_MIN: 3,
            LINE_LENGTH_MAX: 12,
            GROUP_SPREAD: 30,
            GROUP_SPACING: 40,

            INITIAL_CAMERA_DISTANCE: 1200,
            ZOOM_SPEED: 0.1,
            ROTATE_SPEED: 0.01,
            PAN_SPEED: 1.5,
        };

        class BadPerformanceRenderer {
            constructor() {
                this.scene = new THREE.Scene();
                this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 20000);
                this.renderer = new THREE.WebGLRenderer({ antialias: false });

                // 存储所有独立的LineSegments对象 - 这是错误的做法!
                this.lineMeshes = [];

                // 相机控制
                this.orbitTarget = new THREE.Vector3(0, 0, 0);
                this.orbitRadius = CONFIG.INITIAL_CAMERA_DISTANCE;
                this.orbitTheta = 0;
                this.orbitPhi = Math.PI / 4;

                this.init();

                window.badRenderer = this;
            }

            init() {
                this.renderer.setSize(window.innerWidth, window.innerHeight);
                this.renderer.setClearColor(0x1a1a1a);
                document.body.appendChild(this.renderer.domElement);

                this.camera.position.set(300, 300, CONFIG.INITIAL_CAMERA_DISTANCE);
                this.camera.lookAt(0, 0, 0);

                this.setupCameraControls();

                const ambientLight = new THREE.AmbientLight(0x404040, 0.8);
                this.scene.add(ambientLight);

                this.createManyLineSegments();
                this.setupEventListeners();
                this.updateCameraPosition();
                this.animate();
            }

            createManyLineSegments() {
                // console.log('🚨 开始创建大量独立的LineSegments对象...');
                const startTime = performance.now();

                const gridSize = CONFIG.GRID_SIZE;

                for (let groupIndex = 0; groupIndex < CONFIG.MAX_TRANSFORMS; groupIndex++) {
                    // ❌ 每个组都创建独立的几何体和材质 - 严重浪费资源!
                    const geometry = new THREE.BufferGeometry();
                    const positions = new Float32Array(CONFIG.LINES_PER_GROUP * 6);
                    const colors = new Float32Array(CONFIG.LINES_PER_GROUP * 6);

                    // 计算组的世界位置
                    const groupX = (groupIndex % gridSize.X - gridSize.X / 2) * CONFIG.GROUP_SPACING;
                    const groupY = (Math.floor(groupIndex / gridSize.X) % gridSize.Y - gridSize.Y / 2) * CONFIG.GROUP_SPACING;
                    const groupZ = (Math.floor(groupIndex / (gridSize.X * gridSize.Y)) - gridSize.Z / 2) * CONFIG.GROUP_SPACING;

                    // 生成组内的线段
                    for (let i = 0; i < CONFIG.LINES_PER_GROUP; i++) {
                        const localStartX = (Math.random() - 0.5) * CONFIG.GROUP_SPREAD;
                        const localStartY = (Math.random() - 0.5) * CONFIG.GROUP_SPREAD;
                        const localStartZ = (Math.random() - 0.5) * CONFIG.GROUP_SPREAD;

                        const length = CONFIG.LINE_LENGTH_MIN + Math.random() * (CONFIG.LINE_LENGTH_MAX - CONFIG.LINE_LENGTH_MIN);
                        const direction = new THREE.Vector3(
                            Math.random() - 0.5,
                            Math.random() - 0.5,
                            Math.random() - 0.5
                        ).normalize();

                        const localEndX = localStartX + direction.x * length;
                        const localEndY = localStartY + direction.y * length;
                        const localEndZ = localStartZ + direction.z * length;

                        // 直接计算世界坐标 - 没有变换优化
                        const worldStartX = groupX + localStartX;
                        const worldStartY = groupY + localStartY;
                        const worldStartZ = groupZ + localStartZ;
                        const worldEndX = groupX + localEndX;
                        const worldEndY = groupY + localEndY;
                        const worldEndZ = groupZ + localEndZ;

                        const vertexIndex = i * 6;
                        positions[vertexIndex] = worldStartX;
                        positions[vertexIndex + 1] = worldStartY;
                        positions[vertexIndex + 2] = worldStartZ;
                        positions[vertexIndex + 3] = worldEndX;
                        positions[vertexIndex + 4] = worldEndY;
                        positions[vertexIndex + 5] = worldEndZ;

                        // 基于组索引生成颜色
                        const colorSeed = groupIndex * 137;
                        const r = ((colorSeed % 256) / 255) * 0.8 + 0.2;
                        const g = (((colorSeed * 17) % 256) / 255) * 0.8 + 0.2;
                        const b = (((colorSeed * 31) % 256) / 255) * 0.8 + 0.2;

                        colors[vertexIndex] = r;
                        colors[vertexIndex + 1] = g;
                        colors[vertexIndex + 2] = b;
                        colors[vertexIndex + 3] = r;
                        colors[vertexIndex + 4] = g;
                        colors[vertexIndex + 5] = b;
                    }

                    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
                    geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

                    // ❌ 每个组都创建独立的材质 - 浪费GPU资源!
                    const material = new THREE.LineBasicMaterial({
                        vertexColors: true,
                        linewidth: 1 // 注意:大多数平台不支持linewidth > 1
                    });

                    // ❌ 创建独立的LineSegments对象 - 导致大量DrawCall!
                    const lineMesh = new THREE.LineSegments(geometry, material);
                    lineMesh.userData.groupIndex = groupIndex;

                    this.scene.add(lineMesh);
                    this.lineMeshes.push(lineMesh);

                    // 每1000个组输出一次进度,避免控制台刷屏
                    // if ((groupIndex + 1) % 1000 === 0) {
                    //     console.log(`❌ 已创建 ${(groupIndex + 1).toLocaleString()} 个独立LineSegments对象...`);
                    // }
                }

                const endTime = performance.now();
                const creationTime = endTime - startTime;
            }

            setupCameraControls() {
                let isRotating = false;
                let isPanning = false;
                let lastX = 0, lastY = 0;

                // 滚轮缩放
                this.renderer.domElement.addEventListener('wheel', (event) => {
                    event.preventDefault();

                    const delta = event.deltaY * CONFIG.ZOOM_SPEED;
                    this.orbitRadius += delta;
                    this.orbitRadius = Math.max(100, Math.min(10000, this.orbitRadius));
                    this.updateCameraPosition();
                });

                // 鼠标控制
                this.renderer.domElement.addEventListener('mousedown', (event) => {
                    if (event.button === 0) { // 左键 - 旋转
                        isRotating = true;
                        lastX = event.clientX;
                        lastY = event.clientY;
                        event.preventDefault();
                    } else if (event.button === 1) { // 中键 - 平移
                        isPanning = true;
                        lastX = event.clientX;
                        lastY = event.clientY;
                        event.preventDefault();
                        this.renderer.domElement.style.cursor = 'grabbing';
                    }
                });

                this.renderer.domElement.addEventListener('mousemove', (event) => {
                    if (isRotating) {
                        const deltaX = event.clientX - lastX;
                        const deltaY = event.clientY - lastY;

                        this.orbitTheta -= deltaX * CONFIG.ROTATE_SPEED;
                        this.orbitPhi += deltaY * CONFIG.ROTATE_SPEED;
                        this.orbitPhi = Math.max(0.01, Math.min(Math.PI - 0.01, this.orbitPhi));

                        this.updateCameraPosition();

                        lastX = event.clientX;
                        lastY = event.clientY;
                    } else if (isPanning) {
                        const deltaX = event.clientX - lastX;
                        const deltaY = event.clientY - lastY;

                        const right = new THREE.Vector3();
                        const up = new THREE.Vector3(0, 1, 0);

                        right.crossVectors(this.camera.getWorldDirection(new THREE.Vector3()), up).normalize();
                        up.crossVectors(right, this.camera.getWorldDirection(new THREE.Vector3())).normalize();

                        const panVector = right.clone().multiplyScalar(-deltaX * CONFIG.PAN_SPEED)
                            .add(up.clone().multiplyScalar(deltaY * CONFIG.PAN_SPEED));

                        this.orbitTarget.add(panVector);
                        this.updateCameraPosition();

                        lastX = event.clientX;
                        lastY = event.clientY;
                    }
                });

                this.renderer.domElement.addEventListener('mouseup', (event) => {
                    if (event.button === 0) {
                        isRotating = false;
                    } else if (event.button === 1) {
                        isPanning = false;
                        this.renderer.domElement.style.cursor = 'grab';
                    }
                });

                this.renderer.domElement.addEventListener('contextmenu', (e) => e.preventDefault());
            }

            updateCameraPosition() {
                const x = this.orbitTarget.x + this.orbitRadius * Math.sin(this.orbitPhi) * Math.cos(this.orbitTheta);
                const y = this.orbitTarget.y + this.orbitRadius * Math.cos(this.orbitPhi);
                const z = this.orbitTarget.z + this.orbitRadius * Math.sin(this.orbitPhi) * Math.sin(this.orbitTheta);

                this.camera.position.set(x, y, z);
                this.camera.lookAt(this.orbitTarget);
            }

            setupEventListeners() {
                window.addEventListener('resize', () => this.onWindowResize());

                // 只保留基本的键盘快捷键
                document.addEventListener('keydown', (event) => {
                    switch (event.key.toLowerCase()) {
                        case 'r':
                            // 重置相机
                            this.orbitTarget.set(0, 0, 0);
                            this.orbitRadius = CONFIG.INITIAL_CAMERA_DISTANCE;
                            this.orbitTheta = 0;
                            this.orbitPhi = Math.PI / 4;
                            this.updateCameraPosition();
                            break;
                    }
                });
            }

            onWindowResize() {
                this.camera.aspect = window.innerWidth / window.innerHeight;
                this.camera.updateProjectionMatrix();
                this.renderer.setSize(window.innerWidth, window.innerHeight);
            }

            animate() {
                requestAnimationFrame(() => this.animate());

                // 🚨 这里每帧都会对15000+个对象进行渲染处理!
                // 这是导致卡顿的主要原因!
                this.renderer.render(this.scene, this.camera);
            }
        }

        // 性能监控 - 专门监控错误示范的性能问题
        class BadPerformanceMonitor {
            constructor() {
                this.lastTime = performance.now();
                this.frames = 0;
                this.fps = 0;
                this.frameTimeHistory = [];
                this.worstFrameTime = 0;
                this.update();
            }

            update() {
                this.frames++;
                const now = performance.now();
                const frameTime = now - this.lastTime;

                // 记录最差帧时间
                if (frameTime > this.worstFrameTime) {
                    this.worstFrameTime = frameTime;
                }

                this.frameTimeHistory.push(frameTime);
                if (this.frameTimeHistory.length > 60) {
                    this.frameTimeHistory.shift();
                }

                if (now >= this.lastTime + 1000) {
                    this.fps = Math.round((this.frames * 1000) / (now - this.lastTime));
                    this.frames = 0;
                    this.lastTime = now;

                    const fpsElement = document.getElementById('fps');
                    const drawCallsElement = document.getElementById('draw-calls');

                    if (fpsElement && window.badRenderer) {
                        const avgFrameTime = this.frameTimeHistory.reduce((a, b) => a + b, 0) / this.frameTimeHistory.length;
                        fpsElement.textContent = `FPS: ${this.fps} (${avgFrameTime.toFixed(1)}ms/frame)`;

                        // 显示DrawCall数量 - 这是性能问题的根源
                        drawCallsElement.textContent = `Draw Calls: ${window.badRenderer.lineMeshes.length.toLocaleString()}`;

                    }
                }

                requestAnimationFrame(() => this.update());
            }
        }

        document.addEventListener('DOMContentLoaded', () => {
            try {
                const badApp = new BadPerformanceRenderer();
                const monitor = new BadPerformanceMonitor();
            } catch (error) {
                console.error('启动失败:', error);
            }
        });

        // 导出配置
        window.CONFIG = CONFIG;

        // WebGL支持检查
        function checkWebGLSupport() {
            try {
                const canvas = document.createElement('canvas');
                const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
                return !!gl;
            } catch (e) {
                return false;
            }
        }

        if (!checkWebGLSupport()) {
            console.error('❌ WebGL 不支持');
            const warningDiv = document.createElement('div');
            warningDiv.style.cssText = `
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: rgba(255, 0, 0, 0.9);
                color: white;
                padding: 20px;
                border-radius: 10px;
                text-align: center;
                z-index: 1000;
                max-width: 400px;
                border: 3px solid #ff4444;
            `;
            warningDiv.innerHTML = `
                <h3>❌ WebGL 不支持</h3>
                <p>无法运行错误示范版本</p>
                <p>需要支持 WebGL 的浏览器来体验性能问题</p>
            `;
            document.body.appendChild(warningDiv);
        }
    </script>
</body>

</html>
        
编辑器加载中
预览
控制台