{T}

渲染器

渲染器(Renderer)负责将 3D 场景绘制到 2D 画布上。Three.js 提供了多种渲染器,其中 WebGLRenderer 是最常用的。

系统架构概述

plaintext
┌─────────────────────────────────────────────────────────────────────────┐
│                         渲染系统架构                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │                        渲染管线流程                               │   │
│   └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│   Scene ──────▶ Scene Graph ──────▶ Frustum Culling ──────▶             │
│                                                │                       │
│                                                ▼                       │
│   Camera ────────────────────────▶ View Matrix ──────▶                  │
│                                                │                       │
│                                                ▼                       │
│   Lights ────────────────────────────────▶ Shading ──────▶              │
│                                                │                       │
│                                                ▼                       │
│   Materials ───────────────────────────▶ GPU Shader ──────▶             │
│                                                │                       │
│                                                ▼                       │
│   Geometry ─────────────────────────────▶ Vertex Processing ──────▶     │
│                                                │                       │
│                                                ▼                       │
│   Textures ────────────────────────────▶ Fragment Processing ──────▶    │
│                                                │                       │
│                                                ▼                       │
│                          Render Target ◀──────────┘                     │
│                                   │                                     │
│                                   ▼                                     │
│                            Canvas/Screen                                │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

渲染器类型对比

渲染器底层技术主要用途性能特点
WebGLRendererWebGL通用 3D 渲染功能最全
WebGLRenderer (WebGL2)WebGL2现代 3D 渲染更高支持 3D 纹理等
WebGPURendererWebGPU下一代渲染最高实验性
SVGRendererSVG矢量图输出适用于打印
CSS2DRendererCSS/DOM2D 标签-UI 元素
CSS3DRendererCSS 3D3D UI-3D DOM 元素

WebGLRenderer 基础

创建渲染器

javascript
const renderer = new THREE.WebGLRenderer({
  canvas: undefined,           // 指定 canvas 元素
  antialias: true,             // 抗锯齿
  alpha: false,                // 透明背景
  premultipliedAlpha: false,   // 预乘 alpha
  preserveDrawingBuffer: true, // 保留绘制缓冲(截图用)
  powerPreference: 'high-performance', // 性能模式
  stencil: true,               // 模板缓冲
  depth: true,                 // 深度缓冲
  logarithmicDepthBuffer: false // 对数深度缓冲
})

构造参数详解

参数类型默认值说明
canvasHTMLCanvasElement自动创建指定渲染目标
antialiasBooleanfalse开启抗锯齿
alphaBooleanfalse允许透明背景
premultipliedAlphaBooleantrue预乘 alpha 值
preserveDrawingBufferBooleanfalse保留缓冲区(截图必需)
powerPreferenceString'default'GPU 电源模式
stencilBooleantrue模板缓冲区
depthBooleantrue深度缓冲区
logarithmicDepthBufferBooleanfalse对数深度(解决 Z-fighting)
javascript
// powerPreference 可选值
// 'default'     - 浏览器默认
// 'high-performance' - 高性能 GPU
// 'low-power'   - 低功耗 GPU(移动设备)

基本配置

javascript
// 设置渲染尺寸
renderer.setSize(window.innerWidth, window.innerHeight)
 
// 设置像素比(高清屏适配)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
 
// 设置背景色
renderer.setClearColor(0x333333)
renderer.setClearColor(0x333333, 1)  // 第二个参数是透明度
 
// 设置透明背景
renderer.setClearAlpha(0)
 
// 添加到 DOM
document.body.appendChild(renderer.domElement)
 
// 获取 canvas 元素
const canvas = renderer.domElement

渲染器属性

颜色空间配置(重要更新)

⚠️ 版本变更说明:Three.js r152+ 版本中,outputEncoding 已废弃,改用 outputColorSpace

javascript
// ===== Three.js r152+ 版本 =====
// 输出颜色空间
renderer.outputColorSpace = THREE.SRGBColorSpace      // sRGB 颜色空间(推荐)
renderer.outputColorSpace = THREE.LinearSRGBColorSpace // 线性 sRGB(默认)
renderer.outputColorSpace = THREE.DisplayP3ColorSpace  // Display P3 颜色空间
 
// ===== Three.js r151 及更早版本(已废弃)=====
// renderer.outputEncoding = THREE.sRGBEncoding        // 已废弃
// renderer.outputEncoding = THREE.LinearEncoding      // 已废弃
 
// 颜色空间说明
// SRGBColorSpace: 适合直接显示到屏幕,自动进行 gamma 校正
// LinearSRGBColorSpace: 线性空间,适合后处理计算
// DisplayP3ColorSpace: 宽色域,适合 HDR 显示器

色调映射

色调映射用于将 HDR(高动态范围)颜色映射到显示器可显示的范围。

javascript
// 色调映射类型
renderer.toneMapping = THREE.NoToneMapping           // 无色调映射
renderer.toneMapping = THREE.LinearToneMapping       // 线性映射
renderer.toneMapping = THREE.ReinhardToneMapping     // Reinhard 映射
renderer.toneMapping = THREE.CineonToneMapping       // Cineon 映射
renderer.toneMapping = THREE.ACESFilmicToneMapping   // ACES 电影级映射(推荐)
renderer.toneMapping = THREE.CustomToneMapping       // 自定义映射
 
// 曝光度(影响整体亮度)
renderer.toneMappingExposure = 1.0  // 默认值
renderer.toneMappingExposure = 0.5  // 降低曝光
renderer.toneMappingExposure = 2.0  // 提高曝光
色调映射特点适用场景
NoToneMapping无处理HDR 显示器
LinearToneMapping简单线性基础场景
ReinhardToneMapping自然过渡一般场景
CineonToneMapping电影感电影效果
ACESFilmicToneMapping专业电影级推荐、高质量渲染

阴影配置

javascript
// 开启阴影
renderer.shadowMap.enabled = true
 
// 阴影类型
renderer.shadowMap.type = THREE.PCFShadowMap      // PCF 软阴影(默认)
renderer.shadowMap.type = THREE.PCFSoftShadowMap  // PCF 软阴影(更好)
renderer.shadowMap.type = THREE.BasicShadowMap    // 基础阴影(最快)
renderer.shadowMap.type = THREE.VSMShadowMap      // VSM 阴影(最软)
阴影类型质量性能特点
BasicShadowMap★☆☆★★★无过滤,最快
PCFShadowMap★★☆★★☆基础软阴影
PCFSoftShadowMap★★★★☆☆更软的阴影,推荐
VSMShadowMap★★★★★☆方差阴影,边缘最软

其他重要属性

javascript
// ===== 自动清除 =====
renderer.autoClear = true         // 自动清除(颜色+深度+模板)
renderer.autoClearColor = true    // 自动清除颜色
renderer.autoClearDepth = true    // 自动清除深度
renderer.autoClearStencil = true  // 自动清除模板
 
// ===== 对象排序 =====
renderer.sortObjects = true       // 自动排序对象(对透明对象重要)
// 透明对象需要排序才能正确渲染
// 不透明对象可以关闭排序提升性能
 
// ===== 裁剪平面 =====
renderer.clippingPlanes = []                // 裁剪平面数组
renderer.localClippingEnabled = false       // 启用局部裁剪
 
// ===== 信息统计 =====
console.log(renderer.info)
// {
//   memory: {
//     geometries: 1,  // 几何体数量
//     textures: 1     // 纹理数量
//   },
//   render: {
//     calls: 2,       // 绘制调用次数
//     triangles: 36,  // 三角形数量
//     points: 0,      // 点数量
//     lines: 0,       // 线数量
//     frame: 0        // 帧数
//   },
//   programs: 1        // 着色器程序数量
// }

渲染方法

基础渲染

javascript
// 渲染场景
renderer.render(scene, camera)
 
// 渲染循环
function animate() {
  requestAnimationFrame(animate)
  
  // 更新场景状态
  cube.rotation.x += 0.01
  
  // 渲染
  renderer.render(scene, camera)
}
 
animate()

清除画布

javascript
// 清除所有缓冲
renderer.clear()
 
// 清除特定缓冲
renderer.clearColor()     // 清除颜色缓冲
renderer.clearDepth()     // 清除深度缓冲
renderer.clearStencil()   // 清除模板缓冲
 
// 同时清除多个
renderer.clear(true, true, true)  // color, depth, stencil
 
// 清除渲染目标
renderer.clearTarget(renderTarget, color, depth, stencil)

视口与裁剪

javascript
// 设置视口
renderer.setViewport(x, y, width, height)
 
// 设置裁剪区域
renderer.setScissor(x, y, width, height)
renderer.setScissorTest(true)  // 开启裁剪测试
 
// 分屏渲染示例
function renderSplitScreen() {
  const width = window.innerWidth
  const height = window.innerHeight
  
  renderer.setScissorTest(true)
  
  // 左侧视角
  renderer.setViewport(0, 0, width / 2, height)
  renderer.setScissor(0, 0, width / 2, height)
  renderer.render(scene, camera1)
  
  // 右侧视角
  renderer.setViewport(width / 2, 0, width / 2, height)
  renderer.setScissor(width / 2, 0, width / 2, height)
  renderer.render(scene, camera2)
  
  // 小地图
  const mapSize = 150
  renderer.setViewport(width - mapSize, height - mapSize, mapSize, mapSize)
  renderer.setScissor(width - mapSize, height - mapSize, mapSize, mapSize)
  renderer.render(scene, minimapCamera)
  
  renderer.setScissorTest(false)
}

渲染目标(RenderTarget)

渲染到纹理而非屏幕,用于后处理、反射、延迟渲染等高级技术。

基础用法

javascript
// 创建渲染目标
const renderTarget = new THREE.WebGLRenderTarget(width, height, {
  minFilter: THREE.LinearFilter,
  magFilter: THREE.LinearFilter,
  format: THREE.RGBAFormat,
  type: THREE.UnsignedByteType
})
 
// 渲染到目标
renderer.setRenderTarget(renderTarget)
renderer.render(scene, camera)
renderer.setRenderTarget(null)  // 恢复默认(屏幕)
 
// 使用渲染结果作为纹理
const material = new THREE.MeshBasicMaterial({
  map: renderTarget.texture
})

RenderTarget 参数

参数说明推荐值
width宽度根据需求
height高度根据需求
minFilter缩小过滤LinearFilter
magFilter放大过滤LinearFilter
format颜色格式RGBAFormat
type数据类型UnsignedByteType / HalfFloatType
depthBuffer深度缓冲true
stencilBuffer模板缓冲false
samplesMSAA 采样数0 或 4
javascript
// 高质量渲染目标
const hqRenderTarget = new THREE.WebGLRenderTarget(2048, 2048, {
  minFilter: THREE.LinearMipmapLinearFilter,
  magFilter: THREE.LinearFilter,
  format: THREE.RGBAFormat,
  type: THREE.HalfFloatType,
  anisotropy: renderer.capabilities.getMaxAnisotropy()
})
 
// 带深度纹理的渲染目标
const depthRenderTarget = new THREE.WebGLRenderTarget(width, height, {
  minFilter: THREE.NearestFilter,
  magFilter: THREE.NearestFilter,
  format: THREE.DepthFormat,
  type: THREE.UnsignedShortType
})
 
// MSAA 渲染目标(WebGL2)
const msaaRenderTarget = new THREE.WebGLRenderTarget(width, height, {
  samples: 4  // 4x MSAA
})

后处理效果

javascript
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
 
// 创建后处理组合器
const composer = new EffectComposer(renderer)
 
// 添加渲染通道
const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)
 
// 添加泛光效果
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,  // 强度
  0.4,  // 半径
  0.85  // 阈值
)
composer.addPass(bloomPass)
 
// 添加抗锯齿
const fxaaPass = new ShaderPass(FXAAShader)
fxaaPass.uniforms['resolution'].value.set(
  1 / window.innerWidth,
  1 / window.innerHeight
)
composer.addPass(fxaaPass)
 
// 渲染循环使用 composer
function animate() {
  requestAnimationFrame(animate)
  composer.render()
}

自定义后处理着色器

javascript
// 自定义后处理着色器
const CustomShader = {
  uniforms: {
    tDiffuse: { value: null },
    time: { value: 0 },
    resolution: { value: new THREE.Vector2() }
  },
  
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  
  fragmentShader: `
    uniform sampler2D tDiffuse;
    uniform float time;
    uniform vec2 resolution;
    varying vec2 vUv;
    
    void main() {
      vec4 color = texture2D(tDiffuse, vUv);
      
      // 添加扫描线效果
      float scanline = sin(vUv.y * resolution.y * 2.0) * 0.04;
      color.rgb -= scanline;
      
      // 添加色差效果
      float offset = 0.002 * sin(time * 2.0);
      color.r = texture2D(tDiffuse, vUv + vec2(offset, 0.0)).r;
      color.b = texture2D(tDiffuse, vUv - vec2(offset, 0.0)).b;
      
      // 添加暗角
      float vignette = 1.0 - length(vUv - 0.5) * 0.8;
      color.rgb *= vignette;
      
      gl_FragColor = color;
    }
  `
}
 
const customPass = new ShaderPass(CustomShader)
customPass.uniforms['resolution'].value.set(window.innerWidth, window.innerHeight)
composer.addPass(customPass)

多渲染器与多场景

多场景渲染

javascript
// 创建多个场景
const scene3D = new THREE.Scene()
const sceneUI = new THREE.Scene()
 
// 渲染多个场景
function render() {
  // 渲染 3D 场景
  renderer.render(scene3D, camera3D)
  
  // 清除深度缓冲,保留颜色
  renderer.clearDepth()
  
  // 渲染 UI 场景(叠加在 3D 场景上)
  renderer.render(sceneUI, cameraUI)
}

多画布渲染

javascript
// 创建多个渲染器
const renderer1 = new THREE.WebGLRenderer({ canvas: canvas1 })
const renderer2 = new THREE.WebGLRenderer({ canvas: canvas2 })
 
renderer1.setSize(400, 300)
renderer2.setSize(400, 300)
 
// 渲染到不同画布
function render() {
  renderer1.render(scene, camera1)
  renderer2.render(scene, camera2)
}

性能优化

关闭不必要的功能

javascript
// 如果不需要阴影
renderer.shadowMap.enabled = false
 
// 如果不需要排序(只有不透明对象)
renderer.sortObjects = false
 
// 限制像素比(移动设备)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
 
// 或直接设为 1(最低质量,最高性能)
renderer.setPixelRatio(1)

按需渲染

javascript
// 只在需要时渲染
let needsRender = true
 
function animate() {
  requestAnimationFrame(animate)
  
  if (needsRender) {
    renderer.render(scene, camera)
    needsRender = false
  }
}
 
// 触发渲染的事件
controls.addEventListener('change', () => { needsRender = true })
window.addEventListener('resize', () => { needsRender = true })
scene.addEventListener('objectAdded', () => { needsRender = true })

WebGL 上下文丢失处理

javascript
// 监听上下文丢失
renderer.domElement.addEventListener('webglcontextlost', (event) => {
  event.preventDefault()
  console.log('WebGL 上下文丢失')
  // 暂停应用或显示提示
})
 
// 监听上下文恢复
renderer.domElement.addEventListener('webglcontextrestored', () => {
  console.log('WebGL 上下文恢复')
  // 重新初始化场景
  initScene()
})
 
// 预防措施
// 1. 避免过多 WebGL 上下文(标签页过多)
// 2. 合理管理资源
// 3. 移动设备上更注意

性能监控

javascript
class RendererMonitor {
  constructor(renderer) {
    this.renderer = renderer
    this.lastTime = performance.now()
    this.frameCount = 0
    this.fps = 0
  }
  
  update() {
    this.frameCount++
    const currentTime = performance.now()
    
    if (currentTime - this.lastTime >= 1000) {
      this.fps = this.frameCount
      this.frameCount = 0
      this.lastTime = currentTime
      
      const info = this.renderer.info
      console.log({
        fps: this.fps,
        drawCalls: info.render.calls,
        triangles: info.render.triangles,
        geometries: info.memory.geometries,
        textures: info.memory.textures
      })
    }
  }
}
 
const monitor = new RendererMonitor(renderer)
 
function animate() {
  monitor.update()
  renderer.render(scene, camera)
}

高级特性

VR 渲染

javascript
import { VRButton } from 'three/examples/jsm/webxr/VRButton.js'
 
// 开启 VR
renderer.xr.enabled = true
document.body.appendChild(VRButton.createButton(renderer))
 
// VR 渲染循环
renderer.setAnimationLoop(() => {
  // 更新场景
  renderer.render(scene, camera)
})

立体渲染

javascript
import { StereoEffect } from 'three/examples/jsm/effects/StereoEffect.js'
 
const effect = new StereoEffect(renderer)
effect.setSize(window.innerWidth, window.innerHeight)
 
function animate() {
  requestAnimationFrame(animate)
  effect.render(scene, camera)
}

访问 WebGL 上下文

javascript
// 获取 WebGL 上下文
const gl = renderer.getContext()
 
// 查询 WebGL 信息
console.log('WebGL Version:', gl.getParameter(gl.VERSION))
console.log('Vendor:', gl.getParameter(gl.VENDOR))
console.log('Renderer:', gl.getParameter(gl.RENDERER))
console.log('Max Texture Size:', gl.getParameter(gl.MAX_TEXTURE_SIZE))
console.log('Max Vertex Attribs:', gl.getParameter(gl.MAX_VERTEX_ATTRIBS))
 
// 查询扩展
const extensions = {
  anisotropy: gl.getExtension('EXT_texture_filter_anisotropic'),
  floatTextures: gl.getExtension('OES_texture_float'),
  floatLinear: gl.getExtension('OES_texture_float_linear'),
  depthTexture: gl.getExtension('WEBGL_depth_texture')
}
 
// 使用各向异性过滤
if (extensions.anisotropy) {
  const maxAnisotropy = gl.getParameter(
    extensions.anisotropy.MAX_TEXTURE_MAX_ANISOTROPY_EXT
  )
  texture.anisotropy = maxAnisotropy
}

其他渲染器

SVGRenderer

将场景渲染为 SVG,适用于需要矢量输出的场景。

javascript
import { SVGRenderer } from 'three/examples/jsm/renderers/SVGRenderer.js'
 
const renderer = new SVGRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
 
renderer.render(scene, camera)
 
// 导出 SVG
const svgData = renderer.domElement.outerHTML
const blob = new Blob([svgData], { type: 'image/svg+xml' })

CSS2DRenderer

渲染 CSS 2D 元素,用于标签、标注等。

javascript
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'
 
const labelRenderer = new CSS2DRenderer()
labelRenderer.setSize(window.innerWidth, window.innerHeight)
labelRenderer.domElement.style.position = 'absolute'
labelRenderer.domElement.style.top = '0'
labelRenderer.domElement.style.pointerEvents = 'none'
document.body.appendChild(labelRenderer.domElement)
 
// 创建标签
const labelDiv = document.createElement('div')
labelDiv.className = 'label'
labelDiv.textContent = 'Hello World'
labelDiv.style.color = 'white'
labelDiv.style.backgroundColor = 'rgba(0,0,0,0.5)'
labelDiv.style.padding = '5px'
 
const label = new CSS2DObject(labelDiv)
label.position.set(0, 1, 0)
mesh.add(label)
 
// 渲染循环
function animate() {
  renderer.render(scene, camera)
  labelRenderer.render(scene, camera)
}

CSS3DRenderer

渲染 CSS 3D 元素,用于 3D UI。

javascript
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js'
 
const css3DRenderer = new CSS3DRenderer()
css3DRenderer.setSize(window.innerWidth, window.innerHeight)
css3DRenderer.domElement.style.position = 'absolute'
css3DRenderer.domElement.style.top = '0'
css3DRenderer.domElement.style.pointerEvents = 'none'
document.body.appendChild(css3DRenderer.domElement)
 
// 创建 3D UI 元素
const element = document.createElement('div')
element.style.width = '100px'
element.style.height = '100px'
element.style.background = 'red'
element.style.opacity = '0.8'
 
const object = new CSS3DObject(element)
object.position.set(0, 0, 0)
object.rotation.y = Math.PI / 4
scene.add(object)

WebGPU 渲染器(实验性)

⚠️ 注意:WebGPU 是实验性功能,需要浏览器支持。

javascript
import WebGPU from 'three/examples/jsm/capabilities/WebGPU.js'
import WebGPURenderer from 'three/examples/jsm/renderers/webgpu/WebGPURenderer.js'
 
// 检查 WebGPU 支持
if (WebGPU.isAvailable()) {
  const renderer = new WebGPURenderer({
    antialias: true
  })
  
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setPixelRatio(window.devicePixelRatio)
  document.body.appendChild(renderer.domElement)
  
  // 渲染
  renderer.render(scene, camera)
} else {
  console.warn('WebGPU 不可用,回退到 WebGL')
  // 使用 WebGLRenderer
}

截图功能

javascript
// 基础截图
function takeScreenshot() {
  // 需要设置 preserveDrawingBuffer: true
  const dataURL = renderer.domElement.toDataURL('image/png')
  
  const link = document.createElement('a')
  link.download = 'screenshot.png'
  link.href = dataURL
  link.click()
}
 
// 高分辨率截图
function takeHighResScreenshot(scale = 2) {
  const width = window.innerWidth * scale
  const height = window.innerHeight * scale
  
  // 临时修改渲染器尺寸
  renderer.setSize(width, height)
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  
  // 渲染
  renderer.render(scene, camera)
  
  // 截图
  const dataURL = renderer.domElement.toDataURL('image/png')
  
  // 恢复原始尺寸
  renderer.setSize(window.innerWidth, window.innerHeight)
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  
  // 下载
  const link = document.createElement('a')
  link.download = 'screenshot_hires.png'
  link.href = dataURL
  link.click()
}
 
// 截取特定区域
function takeRegionScreenshot(x, y, width, height) {
  const canvas = document.createElement('canvas')
  canvas.width = width
  canvas.height = height
  const ctx = canvas.getContext('2d')
  
  // 从 WebGL canvas 复制像素
  const imageData = renderer.domElement.getContext('webgl')
    .readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(width * height * 4))
  
  ctx.putImageData(new ImageData(new Uint8ClampedArray(imageData), width, height), 0, 0)
  
  return canvas.toDataURL('image/png')
}

常见问题

Q1: 渲染器大小不正确?

javascript
// 确保 setSize 使用正确的尺寸
renderer.setSize(container.clientWidth, container.clientHeight)
 
// 响应窗口变化
window.addEventListener('resize', () => {
  const width = window.innerWidth
  const height = window.innerHeight
  
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  
  renderer.setSize(width, height)
})
 
// 注意:renderer.domElement 尺寸可能受 CSS 影响
// 确保 canvas 的 CSS 不会改变其显示尺寸

Q2: 画面模糊?

javascript
// 1. 设置正确的像素比
renderer.setPixelRatio(window.devicePixelRatio)
 
// 2. 开启抗锯齿
const renderer = new THREE.WebGLRenderer({ antialias: true })
 
// 3. 使用后处理抗锯齿
import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js'
const smaaPass = new SMAAPass(width, height)
composer.addPass(smaaPass)
 
// 4. 确保纹理尺寸足够
texture.minFilter = THREE.LinearMipmapLinearFilter
texture.anisotropy = renderer.capabilities.getMaxAnisotropy()

Q3: 性能问题?

javascript
// 1. 降低像素比
renderer.setPixelRatio(1)  // 移动设备推荐
 
// 2. 减少绘制调用
// - 合并几何体
// - 使用 InstancedMesh
// - 减少材质切换
 
// 3. 按需渲染
let needsRender = true
 
// 4. 检查性能瓶颈
console.log(renderer.info)
 
// 5. 使用简单材质测试
material = new THREE.MeshBasicMaterial()  // 最快
 
// 6. 关闭不必要的功能
renderer.shadowMap.enabled = false

Q4: 截图是空白或黑色?

javascript
// 原因:preserveDrawingBuffer 未开启
// 解决方案 1:创建渲染器时设置
const renderer = new THREE.WebGLRenderer({
  preserveDrawingBuffer: true
})
 
// 解决方案 2:在渲染后立即截图
function animate() {
  renderer.render(scene, camera)
  
  // 渲染后立即截图
  if (needScreenshot) {
    const dataURL = renderer.domElement.toDataURL('image/png')
    // ...
    needScreenshot = false
  }
}

最佳实践

1. 标准初始化模板

javascript
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: false,
  powerPreference: 'high-performance',
  preserveDrawingBuffer: false
})
 
// 基础配置
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
 
// 颜色空间(r152+)
renderer.outputColorSpace = THREE.SRGBColorSpace
 
// 色调映射
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 1.0
 
// 阴影
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
 
// 添加到 DOM
document.body.appendChild(renderer.domElement)

2. 资源清理

javascript
function disposeRenderer() {
  // 清理场景资源
  scene.traverse((child) => {
    if (child.geometry) {
      child.geometry.dispose()
    }
    
    if (child.material) {
      const materials = Array.isArray(child.material) 
        ? child.material 
        : [child.material]
      
      materials.forEach((mat) => {
        // 清理纹理
        for (const key in mat) {
          if (mat[key] && typeof mat[key].dispose === 'function') {
            mat[key].dispose()
          }
        }
        mat.dispose()
      })
    }
  })
  
  // 清理渲染器
  renderer.dispose()
  
  // 移除 DOM
  renderer.domElement.remove()
}

3. 封装渲染管理器

javascript
class RendererManager {
  constructor(options = {}) {
    this.renderer = this.createRenderer(options)
    this.setupResize()
  }
  
  createRenderer(options) {
    const renderer = new THREE.WebGLRenderer({
      antialias: options.antialias ?? true,
      alpha: options.alpha ?? false,
      powerPreference: 'high-performance'
    })
    
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
    renderer.outputColorSpace = THREE.SRGBColorSpace
    renderer.toneMapping = THREE.ACESFilmicToneMapping
    
    if (options.shadows) {
      renderer.shadowMap.enabled = true
      renderer.shadowMap.type = THREE.PCFSoftShadowMap
    }
    
    return renderer
  }
  
  setupResize() {
    window.addEventListener('resize', () => {
      this.renderer.setSize(window.innerWidth, window.innerHeight)
    })
  }
  
  render(scene, camera) {
    this.renderer.render(scene, camera)
  }
  
  dispose() {
    this.renderer.dispose()
  }
  
  get domElement() {
    return this.renderer.domElement
  }
}

相关链接

下一步