threejs-tresjs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThree.js / TresJS Development Skill
Three.js / TresJS 开发技能
File Organization: This skill uses split structure. Seefor advanced patterns and security examples.references/
文件结构:本技能采用拆分式结构。有关高级模式和安全示例,请查看目录。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 基本原则
- TDD First: Write tests before implementation - verify 3D components render correctly
- Performance Aware: Optimize for 60fps with instancing, LOD, and efficient render loops
- Resource Management: Always dispose of geometries, materials, and textures to prevent memory leaks
- Vue Reactivity Integration: Use TresJS for seamless Vue 3 composition API integration
- Safe Color Parsing: Validate color inputs to prevent ReDoS attacks
- GPU Protection: Implement safeguards against GPU resource exhaustion
- Accessibility: Provide fallbacks for devices without WebGL support
- 测试驱动开发优先:在实现前编写测试 - 验证3D组件渲染正确
- 性能感知:通过实例化、LOD(细节层次)和高效渲染循环优化至60fps
- 资源管理:始终释放几何体、材质和纹理以防止内存泄漏
- Vue响应式集成:使用TresJS实现与Vue 3组合API的无缝集成
- 安全颜色解析:验证颜色输入以防止ReDoS攻击
- GPU保护:实施防止GPU资源耗尽的防护措施
- 可访问性:为不支持WebGL的设备提供降级方案
3. Technology Stack & Versions
3. 技术栈与版本
3.1 Recommended Versions
3.1 推荐版本
| Package | Version | Security Notes |
|---|---|---|
| three | ^0.160.0+ | Latest stable, fixes CVE-2020-28496 ReDoS |
| @tresjs/core | ^4.0.0 | Vue 3 integration |
| @tresjs/cientos | ^3.0.0 | Component library |
| postprocessing | ^6.0.0 | Effects library |
| 包名 | 版本 | 安全说明 |
|---|---|---|
| three | ^0.160.0+ | 最新稳定版,修复了CVE-2020-28496 ReDoS漏洞 |
| @tresjs/core | ^4.0.0 | Vue 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 textStep 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
undefinedRun 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
undefinednpm run test:perf
undefined5.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 memorytypescript
// 推荐:为多个精灵使用单个纹理图集
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-screentypescript
// 推荐:启用视锥体剔除(默认开启,但需验证)
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 possibletypescript
// 推荐:简洁优化的着色器
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 已知漏洞
| CVE | Severity | Description | Mitigation |
|---|---|---|---|
| CVE-2020-28496 | HIGH | ReDoS in color parsing | Update to 0.125.0+, validate colors |
| CVE-2022-0177 | MEDIUM | XSS in docs | Update 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 Category | Risk | Mitigation |
|---|---|---|
| A05 Injection | MEDIUM | Validate all color/text inputs |
| A06 Vulnerable Components | HIGH | Keep 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:
- Security: Validate all inputs, especially colors to prevent ReDoS
- Memory: Always dispose resources on component unmount
- Performance: Use instancing, avoid allocations in render loop
- Integration: TresJS provides seamless Vue 3 reactivity
Remember: WebGL has direct GPU access - always validate inputs and manage resources carefully.
References:
- - Complex 3D patterns
references/advanced-patterns.md - - WebGL security practices
references/security-examples.md
Three.js/TresJS为JARVIS HUD提供3D渲染能力:
- 安全:验证所有输入,尤其是颜色以防止ReDoS攻击
- 内存:组件卸载时始终释放资源
- 性能:使用实例化,避免在渲染循环中分配内存
- 集成:TresJS提供与Vue 3响应式的无缝集成
注意:WebGL直接访问GPU - 始终验证输入并谨慎管理资源。
参考资料:
- - 复杂3D模式
references/advanced-patterns.md - - WebGL安全实践
references/security-examples.md