threejs-tresjs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Three.js / TresJS Development Skill

Three.js / TresJS 开发技能

File Organization: This skill uses split structure. See
references/
for advanced patterns and security examples.
文件结构:本技能采用拆分式结构。有关高级模式和安全示例,请查看
references/
目录。

1. Overview

1. 概述

This skill provides expertise for building 3D HUD interfaces using Three.js and TresJS (Vue 3 integration). It focuses on creating performant, visually stunning holographic displays for the JARVIS AI Assistant.
Risk Level: MEDIUM - GPU resource consumption, potential ReDoS in color parsing, WebGL security considerations
Primary Use Cases:
  • Rendering 3D holographic HUD panels
  • Animated status indicators and gauges
  • Particle effects for system visualization
  • Real-time metric displays with 3D elements
本技能提供使用Three.js和TresJS(Vue 3集成)构建3D HUD界面的专业方案,专注于为JARVIS AI助手创建高性能、视觉震撼的全息显示效果。
风险等级:中等 - GPU资源消耗、颜色解析中潜在的ReDoS攻击、WebGL安全考量
主要用例:
  • 渲染3D全息HUD面板
  • 动画状态指示器和仪表盘
  • 用于系统可视化的粒子效果
  • 带有3D元素的实时指标显示

2. Core Responsibilities

2. 核心职责

2.1 Fundamental Principles

2.1 基本原则

  1. TDD First: Write tests before implementation - verify 3D components render correctly
  2. Performance Aware: Optimize for 60fps with instancing, LOD, and efficient render loops
  3. Resource Management: Always dispose of geometries, materials, and textures to prevent memory leaks
  4. Vue Reactivity Integration: Use TresJS for seamless Vue 3 composition API integration
  5. Safe Color Parsing: Validate color inputs to prevent ReDoS attacks
  6. GPU Protection: Implement safeguards against GPU resource exhaustion
  7. Accessibility: Provide fallbacks for devices without WebGL support
  1. 测试驱动开发优先:在实现前编写测试 - 验证3D组件渲染正确
  2. 性能感知:通过实例化、LOD(细节层次)和高效渲染循环优化至60fps
  3. 资源管理:始终释放几何体、材质和纹理以防止内存泄漏
  4. Vue响应式集成:使用TresJS实现与Vue 3组合API的无缝集成
  5. 安全颜色解析:验证颜色输入以防止ReDoS攻击
  6. GPU保护:实施防止GPU资源耗尽的防护措施
  7. 可访问性:为不支持WebGL的设备提供降级方案

3. Technology Stack & Versions

3. 技术栈与版本

3.1 Recommended Versions

3.1 推荐版本

PackageVersionSecurity Notes
three^0.160.0+Latest stable, fixes CVE-2020-28496 ReDoS
@tresjs/core^4.0.0Vue 3 integration
@tresjs/cientos^3.0.0Component library
postprocessing^6.0.0Effects library
包名版本安全说明
three^0.160.0+最新稳定版,修复了CVE-2020-28496 ReDoS漏洞
@tresjs/core^4.0.0Vue 3集成库
@tresjs/cientos^3.0.0组件库
postprocessing^6.0.0特效库

3.2 Security-Critical Updates

3.2 安全关键更新

json
{
  "dependencies": {
    "three": "^0.160.0",
    "@tresjs/core": "^4.0.0",
    "@tresjs/cientos": "^3.0.0"
  }
}
Note: Versions before 0.137.0 have XSS vulnerabilities, before 0.125.0 have ReDoS vulnerabilities.
json
{
  "dependencies": {
    "three": "^0.160.0",
    "@tresjs/core": "^4.0.0",
    "@tresjs/cientos": "^3.0.0"
  }
}
注意:0.137.0之前的版本存在XSS漏洞,0.125.0之前的版本存在ReDoS漏洞。

4. Implementation Patterns

4. 实现模式

4.1 Basic HUD Scene Setup

4.1 基础HUD场景搭建

vue
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'

const gl = {
  clearColor: '#000011',
  alpha: true,
  antialias: true,
  powerPreference: 'high-performance'
}
</script>

<template>
  <TresCanvas v-bind="gl">
    <TresPerspectiveCamera :position="[0, 0, 5]" />
    <OrbitControls :enable-damping="true" />

    <HUDPanels />
    <MetricsDisplay />
    <ParticleEffects />
  </TresCanvas>
</template>
vue
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'

const gl = {
  clearColor: '#000011',
  alpha: true,
  antialias: true,
  powerPreference: 'high-performance'
}
</script>

<template>
  <TresCanvas v-bind="gl">
    <TresPerspectiveCamera :position="[0, 0, 5]" />
    <OrbitControls :enable-damping="true" />

    <HUDPanels />
    <MetricsDisplay />
    <ParticleEffects />
  </TresCanvas>
</template>

4.2 Secure Color Handling

4.2 安全颜色处理

typescript
// utils/safeColor.ts
import { Color } from 'three'

// ✅ Safe color parsing with validation
export function safeParseColor(input: string): Color {
  // Validate format to prevent ReDoS
  const hexPattern = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/
  const rgbPattern = /^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/

  if (!hexPattern.test(input) && !rgbPattern.test(input)) {
    console.warn('Invalid color format, using default')
    return new Color(0x00ff00)  // Default JARVIS green
  }

  return new Color(input)
}

// ❌ DANGEROUS - User input directly to Color
// const color = new Color(userInput)  // Potential ReDoS

// ✅ SECURE - Validated input
const color = safeParseColor(userInput)
typescript
// utils/safeColor.ts
import { Color } from 'three'

// ✅ 带验证的安全颜色解析
export function safeParseColor(input: string): Color {
  // 验证格式以防止ReDoS
  const hexPattern = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/
  const rgbPattern = /^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/

  if (!hexPattern.test(input) && !rgbPattern.test(input)) {
    console.warn('无效颜色格式,使用默认值')
    return new Color(0x00ff00)  // 默认JARVIS绿色
  }

  return new Color(input)
}

// ❌ 危险 - 直接将用户输入传入Color
// const color = new Color(userInput)  // 潜在ReDoS风险

// ✅ 安全 - 经过验证的输入
const color = safeParseColor(userInput)

4.3 Memory-Safe Component

4.3 内存安全组件

vue
<script setup lang="ts">
import { onUnmounted, shallowRef } from 'vue'
import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'

// ✅ Use shallowRef for Three.js objects
const meshRef = shallowRef<Mesh | null>(null)

// ✅ Cleanup on unmount
onUnmounted(() => {
  if (meshRef.value) {
    meshRef.value.geometry.dispose()
    if (Array.isArray(meshRef.value.material)) {
      meshRef.value.material.forEach(m => m.dispose())
    } else {
      meshRef.value.material.dispose()
    }
  }
})
</script>

<template>
  <TresMesh ref="meshRef">
    <TresBoxGeometry :args="[1, 1, 1]" />
    <TresMeshStandardMaterial color="#00ff41" />
  </TresMesh>
</template>
vue
<script setup lang="ts">
import { onUnmounted, shallowRef } from 'vue'
import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'

// ✅ 为Three.js对象使用shallowRef
const meshRef = shallowRef<Mesh | null>(null)

// ✅ 组件卸载时清理资源
onUnmounted(() => {
  if (meshRef.value) {
    meshRef.value.geometry.dispose()
    if (Array.isArray(meshRef.value.material)) {
      meshRef.value.material.forEach(m => m.dispose())
    } else {
      meshRef.value.material.dispose()
    }
  }
})
</script>

<template>
  <TresMesh ref="meshRef">
    <TresBoxGeometry :args="[1, 1, 1]" />
    <TresMeshStandardMaterial color="#00ff41" />
  </TresMesh>
</template>

4.4 Performance-Optimized Instancing

4.4 性能优化的实例化

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { InstancedMesh, Object3D, Matrix4 } from 'three'

const instanceCount = 1000
const instancedMeshRef = ref<InstancedMesh | null>(null)

onMounted(() => {
  if (!instancedMeshRef.value) return

  const dummy = new Object3D()
  const matrix = new Matrix4()

  // ✅ Batch updates for performance
  for (let i = 0; i < instanceCount; i++) {
    dummy.position.set(
      Math.random() * 10 - 5,
      Math.random() * 10 - 5,
      Math.random() * 10 - 5
    )
    dummy.updateMatrix()
    instancedMeshRef.value.setMatrixAt(i, dummy.matrix)
  }

  instancedMeshRef.value.instanceMatrix.needsUpdate = true
})
</script>

<template>
  <TresInstancedMesh ref="instancedMeshRef" :args="[null, null, instanceCount]">
    <TresSphereGeometry :args="[0.05, 8, 8]" />
    <TresMeshBasicMaterial color="#00ff41" />
  </TresInstancedMesh>
</template>
vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { InstancedMesh, Object3D, Matrix4 } from 'three'

const instanceCount = 1000
const instancedMeshRef = ref<InstancedMesh | null>(null)

onMounted(() => {
  if (!instancedMeshRef.value) return

  const dummy = new Object3D()
  const matrix = new Matrix4()

  // ✅ 批量更新以提升性能
  for (let i = 0; i < instanceCount; i++) {
    dummy.position.set(
      Math.random() * 10 - 5,
      Math.random() * 10 - 5,
      Math.random() * 10 - 5
    )
    dummy.updateMatrix()
    instancedMeshRef.value.setMatrixAt(i, dummy.matrix)
  }

  instancedMeshRef.value.instanceMatrix.needsUpdate = true
})
</script>

<template>
  <TresInstancedMesh ref="instancedMeshRef" :args="[null, null, instanceCount]">
    <TresSphereGeometry :args="[0.05, 8, 8]" />
    <TresMeshBasicMaterial color="#00ff41" />
  </TresInstancedMesh>
</template>

4.5 HUD Panel with Text

4.5 带文本的HUD面板

vue
<script setup lang="ts">
import { Text } from '@tresjs/cientos'

const props = defineProps<{
  title: string
  value: number
}>()

// ✅ Sanitize text content
const safeTitle = computed(() =>
  props.title.replace(/[<>]/g, '').slice(0, 50)
)
</script>

<template>
  <TresGroup>
    <!-- Panel background -->
    <TresMesh>
      <TresPlaneGeometry :args="[2, 1]" />
      <TresMeshBasicMaterial
        color="#001122"
        :transparent="true"
        :opacity="0.8"
      />
    </TresMesh>

    <!-- Title text -->
    <Text
      :text="safeTitle"
      :font-size="0.15"
      color="#00ff41"
      :position="[-0.8, 0.3, 0.01]"
    />

    <!-- Value display -->
    <Text
      :text="String(props.value)"
      :font-size="0.3"
      color="#ffffff"
      :position="[0, -0.1, 0.01]"
    />
  </TresGroup>
</template>
vue
<script setup lang="ts">
import { Text } from '@tresjs/cientos'

const props = defineProps<{
  title: string
  value: number
}>()

// ✅ 清理文本内容
const safeTitle = computed(() =>
  props.title.replace(/[<>]/g, '').slice(0, 50)
)
</script>

<template>
  <TresGroup>
    <!-- 面板背景 -->
    <TresMesh>
      <TresPlaneGeometry :args="[2, 1]" />
      <TresMeshBasicMaterial
        color="#001122"
        :transparent="true"
        :opacity="0.8"
      />
    </TresMesh>

    <!-- 标题文本 -->
    <Text
      :text="safeTitle"
      :font-size="0.15"
      color="#00ff41"
      :position="[-0.8, 0.3, 0.01]"
    />

    <!-- 数值显示 -->
    <Text
      :text="String(props.value)"
      :font-size="0.3"
      color="#ffffff"
      :position="[0, -0.1, 0.01]"
    />
  </TresGroup>
</template>

5. Implementation Workflow (TDD)

5. 实现工作流(测试驱动开发)

5.1 TDD Process for 3D Components

5.1 3D组件的测试驱动开发流程

Step 1: Write Failing Test First
typescript
// tests/components/hud-panel.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { mount, VueWrapper } from '@vue/test-utils'
import { Scene, WebGLRenderer } from 'three'
import HUDPanel from '~/components/hud/HUDPanel.vue'

describe('HUDPanel', () => {
  let wrapper: VueWrapper

  beforeEach(() => {
    // Mock WebGL context for testing
    const canvas = document.createElement('canvas')
    const gl = canvas.getContext('webgl2')
    vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(gl)
  })

  afterEach(() => {
    wrapper?.unmount()
    vi.restoreAllMocks()
  })

  it('renders panel with correct dimensions', () => {
    wrapper = mount(HUDPanel, {
      props: { width: 2, height: 1, title: 'Status' }
    })

    // Test fails until component is implemented
    expect(wrapper.exists()).toBe(true)
  })

  it('disposes resources on unmount', async () => {
    wrapper = mount(HUDPanel, {
      props: { width: 2, height: 1, title: 'Status' }
    })

    const disposeSpy = vi.fn()
    wrapper.vm.meshRef.geometry.dispose = disposeSpy

    wrapper.unmount()
    expect(disposeSpy).toHaveBeenCalled()
  })
})
Step 2: Implement Minimum to Pass
vue
<script setup lang="ts">
import { shallowRef, onUnmounted } from 'vue'
import { Mesh } from 'three'

const props = defineProps<{
  width: number
  height: number
  title: string
}>()

const meshRef = shallowRef<Mesh | null>(null)

onUnmounted(() => {
  if (meshRef.value) {
    meshRef.value.geometry.dispose()
    ;(meshRef.value.material as any).dispose()
  }
})
</script>

<template>
  <TresMesh ref="meshRef">
    <TresPlaneGeometry :args="[props.width, props.height]" />
    <TresMeshBasicMaterial color="#001122" :transparent="true" :opacity="0.8" />
  </TresMesh>
</template>
Step 3: Refactor Following Patterns
typescript
// After tests pass, add performance optimizations
// - Use instancing for multiple panels
// - Add LOD for distant panels
// - Implement texture atlases for text
Step 4: Run Full Verification
bash
undefined
步骤1:先编写失败的测试
typescript
// tests/components/hud-panel.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { mount, VueWrapper } from '@vue/test-utils'
import { Scene, WebGLRenderer } from 'three'
import HUDPanel from '~/components/hud/HUDPanel.vue'

describe('HUDPanel', () => {
  let wrapper: VueWrapper

  beforeEach(() => {
    // 为测试模拟WebGL上下文
    const canvas = document.createElement('canvas')
    const gl = canvas.getContext('webgl2')
    vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(gl)
  })

  afterEach(() => {
    wrapper?.unmount()
    vi.restoreAllMocks()
  })

  it('渲染具有正确尺寸的面板', () => {
    wrapper = mount(HUDPanel, {
      props: { width: 2, height: 1, title: '状态' }
    })

    // 组件实现前测试会失败
    expect(wrapper.exists()).toBe(true)
  })

  it('卸载时释放资源', async () => {
    wrapper = mount(HUDPanel, {
      props: { width: 2, height: 1, title: '状态' }
    })

    const disposeSpy = vi.fn()
    wrapper.vm.meshRef.geometry.dispose = disposeSpy

    wrapper.unmount()
    expect(disposeSpy).toHaveBeenCalled()
  })
})
步骤2:实现最小功能以通过测试
vue
<script setup lang="ts">
import { shallowRef, onUnmounted } from 'vue'
import { Mesh } from 'three'

const props = defineProps<{
  width: number
  height: number
  title: string
}>()

const meshRef = shallowRef<Mesh | null>(null)

onUnmounted(() => {
  if (meshRef.value) {
    meshRef.value.geometry.dispose()
    ;(meshRef.value.material as any).dispose()
  }
})
</script>

<template>
  <TresMesh ref="meshRef">
    <TresPlaneGeometry :args="[props.width, props.height]" />
    <TresMeshBasicMaterial color="#001122" :transparent="true" :opacity="0.8" />
  </TresMesh>
</template>
步骤3:遵循模式进行重构
typescript
// 测试通过后,添加性能优化
// - 为多个面板使用实例化
// - 为远距离面板添加LOD
// 为文本实现纹理图集
步骤4:运行完整验证
bash
undefined

Run all tests

运行所有测试

npm test
npm test

Run with coverage

带覆盖率运行

npm test -- --coverage
npm test -- --coverage

Type check

类型检查

npm run typecheck
npm run typecheck

Performance benchmark

性能基准测试

npm run test:perf
undefined
npm run test:perf
undefined

5.2 Testing 3D Animations

5.2 测试3D动画

typescript
import { describe, it, expect, vi } from 'vitest'
import { useRenderLoop } from '@tresjs/core'

describe('Animation Loop', () => {
  it('maintains 60fps during animation', async () => {
    const frameTimes: number[] = []
    let lastTime = performance.now()

    const { onLoop } = useRenderLoop()

    onLoop(() => {
      const now = performance.now()
      frameTimes.push(now - lastTime)
      lastTime = now
    })

    // Simulate 60 frames
    await new Promise(resolve => setTimeout(resolve, 1000))

    const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length
    expect(avgFrameTime).toBeLessThan(16.67) // 60fps = 16.67ms per frame
  })

  it('cleans up animation loop on unmount', () => {
    const cleanup = vi.fn()
    const { pause } = useRenderLoop()

    // Component unmounts
    pause()

    expect(cleanup).not.toThrow()
  })
})
typescript
import { describe, it, expect, vi } from 'vitest'
import { useRenderLoop } from '@tresjs/core'

describe('动画循环', () => {
  it('动画期间保持60fps', async () => {
    const frameTimes: number[] = []
    let lastTime = performance.now()

    const { onLoop } = useRenderLoop()

    onLoop(() => {
      const now = performance.now()
      frameTimes.push(now - lastTime)
      lastTime = now
    })

    // 模拟60帧
    await new Promise(resolve => setTimeout(resolve, 1000))

    const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length
    expect(avgFrameTime).toBeLessThan(16.67) // 60fps = 每帧16.67ms
  })

  it('卸载时清理动画循环', () => {
    const cleanup = vi.fn()
    const { pause } = useRenderLoop()

    // 组件卸载
    pause()

    expect(cleanup).not.toThrow()
  })
})

5.3 Testing Resource Disposal

5.3 测试资源释放

typescript
describe('Resource Management', () => {
  it('disposes all GPU resources', () => {
    const geometry = new BoxGeometry(1, 1, 1)
    const material = new MeshStandardMaterial({ color: 0x00ff41 })
    const mesh = new Mesh(geometry, material)

    const geoDispose = vi.spyOn(geometry, 'dispose')
    const matDispose = vi.spyOn(material, 'dispose')

    // Cleanup function
    mesh.geometry.dispose()
    mesh.material.dispose()

    expect(geoDispose).toHaveBeenCalled()
    expect(matDispose).toHaveBeenCalled()
  })

  it('handles material arrays correctly', () => {
    const materials = [
      new MeshBasicMaterial(),
      new MeshStandardMaterial()
    ]
    const mesh = new Mesh(new BoxGeometry(), materials)

    const spies = materials.map(m => vi.spyOn(m, 'dispose'))

    materials.forEach(m => m.dispose())

    spies.forEach(spy => expect(spy).toHaveBeenCalled())
  })
})
typescript
describe('资源管理', () => {
  it('释放所有GPU资源', () => {
    const geometry = new BoxGeometry(1, 1, 1)
    const material = new MeshStandardMaterial({ color: 0x00ff41 })
    const mesh = new Mesh(geometry, material)

    const geoDispose = vi.spyOn(geometry, 'dispose')
    const matDispose = vi.spyOn(material, 'dispose')

    // 清理函数
    mesh.geometry.dispose()
    mesh.material.dispose()

    expect(geoDispose).toHaveBeenCalled()
    expect(matDispose).toHaveBeenCalled()
  })

  it('正确处理材质数组', () => {
    const materials = [
      new MeshBasicMaterial(),
      new MeshStandardMaterial()
    ]
    const mesh = new Mesh(new BoxGeometry(), materials)

    const spies = materials.map(m => vi.spyOn(m, 'dispose'))

    materials.forEach(m => m.dispose())

    spies.forEach(spy => expect(spy).toHaveBeenCalled())
  })
})

6. Performance Patterns

6. 性能模式

6.1 Geometry Instancing

6.1 几何体实例化

typescript
// Good: Use InstancedMesh for repeated objects
import { InstancedMesh, Matrix4, Object3D } from 'three'

const COUNT = 1000
const mesh = new InstancedMesh(geometry, material, COUNT)
const dummy = new Object3D()

for (let i = 0; i < COUNT; i++) {
  dummy.position.set(Math.random() * 10, Math.random() * 10, Math.random() * 10)
  dummy.updateMatrix()
  mesh.setMatrixAt(i, dummy.matrix)
}
mesh.instanceMatrix.needsUpdate = true

// Bad: Creating individual meshes
for (let i = 0; i < COUNT; i++) {
  const mesh = new Mesh(geometry.clone(), material.clone()) // Memory waste!
  scene.add(mesh)
}
typescript
// 推荐:对重复对象使用InstancedMesh
import { InstancedMesh, Matrix4, Object3D } from 'three'

const COUNT = 1000
const mesh = new InstancedMesh(geometry, material, COUNT)
const dummy = new Object3D()

for (let i = 0; i < COUNT; i++) {
  dummy.position.set(Math.random() * 10, Math.random() * 10, Math.random() * 10)
  dummy.updateMatrix()
  mesh.setMatrixAt(i, dummy.matrix)
}
mesh.instanceMatrix.needsUpdate = true

// 不推荐:创建单独的网格
for (let i = 0; i < COUNT; i++) {
  const mesh = new Mesh(geometry.clone(), material.clone()) // 内存浪费!
  scene.add(mesh)
}

6.2 Texture Atlases

6.2 纹理图集

typescript
// Good: Single texture atlas for multiple sprites
const atlas = new TextureLoader().load('/textures/hud-atlas.png')
const materials = {
  panel: new SpriteMaterial({ map: atlas }),
  icon: new SpriteMaterial({ map: atlas })
}

// Set UV offsets for different sprites
materials.panel.map.offset.set(0, 0.5)
materials.panel.map.repeat.set(0.5, 0.5)

// Bad: Loading separate textures
const panelTex = new TextureLoader().load('/textures/panel.png')
const iconTex = new TextureLoader().load('/textures/icon.png')
// Multiple draw calls, more GPU memory
typescript
// 推荐:为多个精灵使用单个纹理图集
const atlas = new TextureLoader().load('/textures/hud-atlas.png')
const materials = {
  panel: new SpriteMaterial({ map: atlas }),
  icon: new SpriteMaterial({ map: atlas })
}

// 为不同精灵设置UV偏移
materials.panel.map.offset.set(0, 0.5)
materials.panel.map.repeat.set(0.5, 0.5)

// 不推荐:加载单独纹理
const panelTex = new TextureLoader().load('/textures/panel.png')
const iconTex = new TextureLoader().load('/textures/icon.png')
// 多次绘制调用,占用更多GPU内存

6.3 Level of Detail (LOD)

6.3 细节层次(LOD)

typescript
// Good: Use LOD for complex objects
import { LOD } from 'three'

const lod = new LOD()

// High detail - close up
const highDetail = new Mesh(
  new SphereGeometry(1, 32, 32),
  material
)
lod.addLevel(highDetail, 0)

// Medium detail - mid range
const medDetail = new Mesh(
  new SphereGeometry(1, 16, 16),
  material
)
lod.addLevel(medDetail, 10)

// Low detail - far away
const lowDetail = new Mesh(
  new SphereGeometry(1, 8, 8),
  material
)
lod.addLevel(lowDetail, 20)

scene.add(lod)

// Bad: Always rendering high detail
const sphere = new Mesh(new SphereGeometry(1, 64, 64), material)
typescript
// 推荐:为复杂对象使用LOD
import { LOD } from 'three'

const lod = new LOD()

// 高细节 - 近距离
const highDetail = new Mesh(
  new SphereGeometry(1, 32, 32),
  material
)
lod.addLevel(highDetail, 0)

// 中等细节 - 中距离
const medDetail = new Mesh(
  new SphereGeometry(1, 16, 16),
  material
)
lod.addLevel(medDetail, 10)

// 低细节 - 远距离
const lowDetail = new Mesh(
  new SphereGeometry(1, 8, 8),
  material
)
lod.addLevel(lowDetail, 20)

scene.add(lod)

// 不推荐:始终渲染高细节
const sphere = new Mesh(new SphereGeometry(1, 64, 64), material)

6.4 Frustum Culling

6.4 视锥体剔除

typescript
// Good: Enable frustum culling (default, but verify)
mesh.frustumCulled = true

// For custom bounds optimization
mesh.geometry.computeBoundingSphere()
mesh.geometry.computeBoundingBox()

// Manual visibility check for complex scenes
const frustum = new Frustum()
const matrix = new Matrix4().multiplyMatrices(
  camera.projectionMatrix,
  camera.matrixWorldInverse
)
frustum.setFromProjectionMatrix(matrix)

objects.forEach(obj => {
  obj.visible = frustum.intersectsObject(obj)
})

// Bad: Disabling culling or rendering everything
mesh.frustumCulled = false // Renders even when off-screen
typescript
// 推荐:启用视锥体剔除(默认开启,但需验证)
mesh.frustumCulled = true

// 自定义边界优化
mesh.geometry.computeBoundingSphere()
mesh.geometry.computeBoundingBox()

// 复杂场景的手动可见性检查
const frustum = new Frustum()
const matrix = new Matrix4().multiplyMatrices(
  camera.projectionMatrix,
  camera.matrixWorldInverse
)
frustum.setFromProjectionMatrix(matrix)

objects.forEach(obj => {
  obj.visible = frustum.intersectsObject(obj)
})

// 不推荐:禁用剔除或渲染所有对象
mesh.frustumCulled = false // 即使在屏幕外也会渲染

6.5 Object Pooling

6.5 对象池

typescript
// Good: Pool and reuse objects
class ParticlePool {
  private pool: Mesh[] = []
  private active: Set<Mesh> = new Set()

  constructor(private geometry: BufferGeometry, private material: Material) {
    // Pre-allocate pool
    for (let i = 0; i < 100; i++) {
      const mesh = new Mesh(geometry, material)
      mesh.visible = false
      this.pool.push(mesh)
    }
  }

  acquire(): Mesh | null {
    const mesh = this.pool.find(m => !this.active.has(m))
    if (mesh) {
      mesh.visible = true
      this.active.add(mesh)
      return mesh
    }
    return null
  }

  release(mesh: Mesh): void {
    mesh.visible = false
    this.active.delete(mesh)
  }
}

// Bad: Creating/destroying objects each frame
function spawnParticle() {
  const mesh = new Mesh(geometry, material) // GC pressure!
  scene.add(mesh)
  setTimeout(() => {
    scene.remove(mesh)
    mesh.geometry.dispose()
  }, 1000)
}
typescript
// 推荐:池化并复用对象
class ParticlePool {
  private pool: Mesh[] = []
  private active: Set<Mesh> = new Set()

  constructor(private geometry: BufferGeometry, private material: Material) {
    // 预分配对象池
    for (let i = 0; i < 100; i++) {
      const mesh = new Mesh(geometry, material)
      mesh.visible = false
      this.pool.push(mesh)
    }
  }

  acquire(): Mesh | null {
    const mesh = this.pool.find(m => !this.active.has(m))
    if (mesh) {
      mesh.visible = true
      this.active.add(mesh)
      return mesh
    }
    return null
  }

  release(mesh: Mesh): void {
    mesh.visible = false
    this.active.delete(mesh)
  }
}

// 不推荐:每帧创建/销毁对象
function spawnParticle() {
  const mesh = new Mesh(geometry, material) // 垃圾回收压力!
  scene.add(mesh)
  setTimeout(() => {
    scene.remove(mesh)
    mesh.geometry.dispose()
  }, 1000)
}

6.6 RAF Optimization

6.6 RAF优化

typescript
// Good: Efficient render loop
let lastTime = 0
const targetFPS = 60
const frameInterval = 1000 / targetFPS

function animate(currentTime: number) {
  requestAnimationFrame(animate)

  const delta = currentTime - lastTime

  // Skip frame if too soon (for battery saving)
  if (delta < frameInterval) return

  lastTime = currentTime - (delta % frameInterval)

  // Update only what changed
  if (needsUpdate) {
    updateScene()
    renderer.render(scene, camera)
  }
}

// Bad: Rendering every frame unconditionally
function animate() {
  requestAnimationFrame(animate)

  // Always updates everything
  updateAllObjects()
  renderer.render(scene, camera) // Even if nothing changed
}
typescript
// 推荐:高效渲染循环
let lastTime = 0
const targetFPS = 60
const frameInterval = 1000 / targetFPS

function animate(currentTime: number) {
  requestAnimationFrame(animate)

  const delta = currentTime - lastTime

  // 时间过短则跳过帧(节省电量)
  if (delta < frameInterval) return

  lastTime = currentTime - (delta % frameInterval)

  // 仅更新变化的内容
  if (needsUpdate) {
    updateScene()
    renderer.render(scene, camera)
  }
}

// 不推荐:无条件渲染每帧
function animate() {
  requestAnimationFrame(animate)

  // 始终更新所有对象
  updateAllObjects()
  renderer.render(scene, camera) // 即使没有变化也渲染
}

6.7 Shader Optimization

6.7 着色器优化

typescript
// Good: Simple, optimized shaders
const material = new ShaderMaterial({
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    varying vec2 vUv;
    uniform vec3 color;
    void main() {
      gl_FragColor = vec4(color, 1.0);
    }
  `,
  uniforms: {
    color: { value: new Color(0x00ff41) }
  }
})

// Bad: Complex calculations in fragment shader
// Avoid: loops, conditionals, texture lookups when possible
typescript
// 推荐:简洁优化的着色器
const material = new ShaderMaterial({
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    varying vec2 vUv;
    uniform vec3 color;
    void main() {
      gl_FragColor = vec4(color, 1.0);
    }
  `,
  uniforms: {
    color: { value: new Color(0x00ff41) }
  }
})

// 不推荐:片段着色器中的复杂计算
// 避免:循环、条件判断、不必要的纹理查找

7. Security Standards

7. 安全标准

7.1 Known Vulnerabilities

7.1 已知漏洞

CVESeverityDescriptionMitigation
CVE-2020-28496HIGHReDoS in color parsingUpdate to 0.125.0+, validate colors
CVE-2022-0177MEDIUMXSS in docsUpdate to 0.137.0+
CVE编号严重程度描述缓解措施
CVE-2020-28496颜色解析中的ReDoS漏洞更新至0.125.0+,验证颜色输入
CVE-2022-0177文档中的XSS漏洞更新至0.137.0+

7.2 OWASP Top 10 Coverage

7.2 OWASP Top 10 覆盖

OWASP CategoryRiskMitigation
A05 InjectionMEDIUMValidate all color/text inputs
A06 Vulnerable ComponentsHIGHKeep Three.js updated
OWASP类别风险缓解措施
A05 注入验证所有颜色/文本输入
A06 易受攻击的组件保持Three.js为最新版本

7.3 GPU Resource Protection

7.3 GPU资源保护

typescript
// composables/useResourceLimit.ts
export function useResourceLimit() {
  const MAX_TRIANGLES = 1_000_000
  const MAX_DRAW_CALLS = 100

  let triangleCount = 0

  function checkGeometry(geometry: BufferGeometry): boolean {
    const triangles = geometry.index
      ? geometry.index.count / 3
      : geometry.attributes.position.count / 3

    if (triangleCount + triangles > MAX_TRIANGLES) {
      console.error('Triangle limit exceeded')
      return false
    }

    triangleCount += triangles
    return true
  }

  return { checkGeometry }
}
typescript
// composables/useResourceLimit.ts
export function useResourceLimit() {
  const MAX_TRIANGLES = 1_000_000
  const MAX_DRAW_CALLS = 100

  let triangleCount = 0

  function checkGeometry(geometry: BufferGeometry): boolean {
    const triangles = geometry.index
      ? geometry.index.count / 3
      : geometry.attributes.position.count / 3

    if (triangleCount + triangles > MAX_TRIANGLES) {
      console.error('超过三角形数量限制')
      return false
    }

    triangleCount += triangles
    return true
  }

  return { checkGeometry }
}

6. Testing & Quality

6. 测试与质量

6.1 Component Testing

6.1 组件测试

typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'

describe('HUD Panel', () => {
  it('sanitizes malicious title input', () => {
    const wrapper = mount(HUDPanel, {
      props: {
        title: '<script>alert("xss")</script>Status',
        value: 75
      }
    })

    expect(wrapper.vm.safeTitle).not.toContain('<script>')
  })
})
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'

describe('HUD面板', () => {
  it('清理恶意标题输入', () => {
    const wrapper = mount(HUDPanel, {
      props: {
        title: '<script>alert("xss")</script>状态',
        value: 75
      }
    })

    expect(wrapper.vm.safeTitle).not.toContain('<script>')
  })
})

6.2 Performance Testing

6.2 性能测试

typescript
describe('Instanced Mesh', () => {
  it('handles 1000 instances without frame drop', async () => {
    const scene = new Scene()
    // Setup instanced mesh...

    const startTime = performance.now()
    renderer.render(scene, camera)
    const renderTime = performance.now() - startTime

    expect(renderTime).toBeLessThan(16.67)  // 60fps target
  })
})
typescript
describe('实例化网格', () => {
  it('处理1000个实例而不丢帧', async () => {
    const scene = new Scene()
    // 设置实例化网格...

    const startTime = performance.now()
    renderer.render(scene, camera)
    const renderTime = performance.now() - startTime

    expect(renderTime).toBeLessThan(16.67)  // 目标60fps
  })
})

8. Common Mistakes & Anti-Patterns

8. 常见错误与反模式

8.1 Critical Security Anti-Patterns

8.1 严重安全反模式

Never: Parse User Colors Directly

禁止:直接解析用户颜色

typescript
// ❌ DANGEROUS - ReDoS vulnerability
const color = new Color(userInput)

// ✅ SECURE - Validated input
const color = safeParseColor(userInput)
typescript
// ❌ 危险 - ReDoS漏洞
const color = new Color(userInput)

// ✅ 安全 - 经过验证的输入
const color = safeParseColor(userInput)

Never: Skip Resource Disposal

禁止:跳过资源释放

typescript
// ❌ MEMORY LEAK
const mesh = new Mesh(geometry, material)
scene.remove(mesh)
// Geometry and material still in GPU memory!

// ✅ PROPER CLEANUP
scene.remove(mesh)
mesh.geometry.dispose()
mesh.material.dispose()
typescript
// ❌ 内存泄漏
const mesh = new Mesh(geometry, material)
scene.remove(mesh)
// 几何体和材质仍占用GPU内存!

// ✅ 正确清理
scene.remove(mesh)
mesh.geometry.dispose()
mesh.material.dispose()

8.2 Performance Anti-Patterns

8.2 性能反模式

Avoid: Creating Objects in Render Loop

避免:在渲染循环中创建对象

typescript
// ❌ BAD - Creates garbage every frame
function animate() {
  mesh.position.add(new Vector3(0, 0.01, 0))  // New object every frame!
}

// ✅ GOOD - Reuse objects
const velocity = new Vector3(0, 0.01, 0)
function animate() {
  mesh.position.add(velocity)
}
typescript
// ❌ 不良 - 每帧产生垃圾
function animate() {
  mesh.position.add(new Vector3(0, 0.01, 0))  // 每帧创建新对象!
}

// ✅ 良好 - 复用对象
const velocity = new Vector3(0, 0.01, 0)
function animate() {
  mesh.position.add(velocity)
}

13. Pre-Deployment Checklist

13. 部署前检查清单

Security Verification

安全验证

  • Three.js version >= 0.137.0 (XSS fix)
  • All color inputs validated before parsing
  • No user input directly to
    new Color()
  • Resource limits enforced
  • Three.js版本 >= 0.137.0(修复XSS)
  • 所有颜色输入在解析前已验证
  • 没有直接将用户输入传入
    new Color()
  • 已执行资源限制

Performance Verification

性能验证

  • All geometries/materials disposed on unmount
  • Instancing used for repeated objects
  • No object creation in render loop
  • LOD implemented for complex scenes
  • 所有几何体/材质在卸载时已释放
  • 重复对象使用了实例化
  • 渲染循环中没有对象创建
  • 复杂场景已实现LOD

14. Summary

14. 总结

Three.js/TresJS provides 3D rendering for the JARVIS HUD:
  1. Security: Validate all inputs, especially colors to prevent ReDoS
  2. Memory: Always dispose resources on component unmount
  3. Performance: Use instancing, avoid allocations in render loop
  4. Integration: TresJS provides seamless Vue 3 reactivity
Remember: WebGL has direct GPU access - always validate inputs and manage resources carefully.

References:
  • references/advanced-patterns.md
    - Complex 3D patterns
  • references/security-examples.md
    - WebGL security practices
Three.js/TresJS为JARVIS HUD提供3D渲染能力:
  1. 安全:验证所有输入,尤其是颜色以防止ReDoS攻击
  2. 内存:组件卸载时始终释放资源
  3. 性能:使用实例化,避免在渲染循环中分配内存
  4. 集成:TresJS提供与Vue 3响应式的无缝集成
注意:WebGL直接访问GPU - 始终验证输入并谨慎管理资源。

参考资料:
  • references/advanced-patterns.md
    - 复杂3D模式
  • references/security-examples.md
    - WebGL安全实践