第11章-二次开发进阶

第十一章:二次开发进阶

11.1 自定义加载器

11.1.1 加载器架构

Astral3D的加载器系统采用可扩展的设计,允许开发者注册自定义加载器以支持新的文件格式。

// 加载器基类
abstract class BaseLoader {
  // 支持的文件扩展名
  abstract extensions: string[];
  
  // 检查是否支持该文件
  canLoad(url: string): boolean {
    const ext = url.split('.').pop()?.toLowerCase() || '';
    return this.extensions.includes(ext);
  }
  
  // 加载文件
  abstract load(url: string, options?: LoadOptions): Promise<THREE.Object3D>;
  
  // 解析数据
  abstract parse(data: ArrayBuffer | string): Promise<THREE.Object3D>;
}

11.1.2 创建自定义加载器

import * as THREE from 'three';
import { BaseLoader, LoadOptions, LoaderManager } from '@astral3d/engine';

// 自定义JSON模型加载器
class CustomJSONLoader extends BaseLoader {
  extensions = ['custom', 'cjson'];
  
  async load(url: string, options?: LoadOptions): Promise<THREE.Object3D> {
    const response = await fetch(url);
    const data = await response.json();
    return this.parse(data);
  }
  
  async parse(data: any): Promise<THREE.Object3D> {
    const group = new THREE.Group();
    group.name = data.name || 'CustomModel';
    
    // 解析几何体
    if (data.geometries) {
      for (const geoData of data.geometries) {
        const geometry = this.parseGeometry(geoData);
        const material = this.parseMaterial(geoData.material);
        const mesh = new THREE.Mesh(geometry, material);
        
        if (geoData.position) {
          mesh.position.fromArray(geoData.position);
        }
        if (geoData.rotation) {
          mesh.rotation.fromArray(geoData.rotation);
        }
        if (geoData.scale) {
          mesh.scale.fromArray(geoData.scale);
        }
        
        mesh.name = geoData.name || '';
        group.add(mesh);
      }
    }
    
    return group;
  }
  
  private parseGeometry(data: any): THREE.BufferGeometry {
    const geometry = new THREE.BufferGeometry();
    
    if (data.vertices) {
      geometry.setAttribute(
        'position',
        new THREE.Float32BufferAttribute(data.vertices, 3)
      );
    }
    
    if (data.normals) {
      geometry.setAttribute(
        'normal',
        new THREE.Float32BufferAttribute(data.normals, 3)
      );
    }
    
    if (data.uvs) {
      geometry.setAttribute(
        'uv',
        new THREE.Float32BufferAttribute(data.uvs, 2)
      );
    }
    
    if (data.indices) {
      geometry.setIndex(data.indices);
    }
    
    return geometry;
  }
  
  private parseMaterial(data: any): THREE.Material {
    return new THREE.MeshStandardMaterial({
      color: data?.color || 0xffffff,
      metalness: data?.metalness || 0,
      roughness: data?.roughness || 1
    });
  }
}

// 注册加载器
LoaderManager.register(new CustomJSONLoader());

// 使用
const model = await viewer.loader.load('model.custom');

11.1.3 点云加载器示例

// 点云加载器
class PointCloudLoader extends BaseLoader {
  extensions = ['pts', 'xyz', 'pcd'];
  
  async load(url: string, options?: LoadOptions): Promise<THREE.Points> {
    const response = await fetch(url);
    const text = await response.text();
    
    const ext = url.split('.').pop()?.toLowerCase();
    
    switch (ext) {
      case 'pts':
        return this.parsePTS(text);
      case 'xyz':
        return this.parseXYZ(text);
      default:
        throw new Error(`Unsupported format: ${ext}`);
    }
  }
  
  private parsePTS(text: string): THREE.Points {
    const lines = text.trim().split('\n');
    const count = parseInt(lines[0]);
    
    const positions: number[] = [];
    const colors: number[] = [];
    
    for (let i = 1; i <= count && i < lines.length; i++) {
      const parts = lines[i].trim().split(/\s+/);
      
      // X Y Z R G B Intensity
      positions.push(
        parseFloat(parts[0]),
        parseFloat(parts[1]),
        parseFloat(parts[2])
      );
      
      if (parts.length >= 6) {
        colors.push(
          parseFloat(parts[3]) / 255,
          parseFloat(parts[4]) / 255,
          parseFloat(parts[5]) / 255
        );
      }
    }
    
    return this.createPoints(positions, colors);
  }
  
  private parseXYZ(text: string): THREE.Points {
    const lines = text.trim().split('\n');
    const positions: number[] = [];
    const colors: number[] = [];
    
    for (const line of lines) {
      const parts = line.trim().split(/\s+/);
      if (parts.length >= 3) {
        positions.push(
          parseFloat(parts[0]),
          parseFloat(parts[1]),
          parseFloat(parts[2])
        );
        
        if (parts.length >= 6) {
          colors.push(
            parseFloat(parts[3]) / 255,
            parseFloat(parts[4]) / 255,
            parseFloat(parts[5]) / 255
          );
        }
      }
    }
    
    return this.createPoints(positions, colors);
  }
  
  private createPoints(positions: number[], colors: number[]): THREE.Points {
    const geometry = new THREE.BufferGeometry();
    
    geometry.setAttribute(
      'position',
      new THREE.Float32BufferAttribute(positions, 3)
    );
    
    if (colors.length > 0) {
      geometry.setAttribute(
        'color',
        new THREE.Float32BufferAttribute(colors, 3)
      );
    }
    
    const material = new THREE.PointsMaterial({
      size: 0.01,
      vertexColors: colors.length > 0,
      sizeAttenuation: true
    });
    
    return new THREE.Points(geometry, material);
  }
}

11.2 自定义着色器

11.2.1 着色器材质

import * as THREE from 'three';

// 自定义着色器材质
const customShaderMaterial = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 },
    color1: { value: new THREE.Color(0xff0000) },
    color2: { value: new THREE.Color(0x0000ff) },
    resolution: { value: new THREE.Vector2() }
  },
  
  vertexShader: `
    varying vec2 vUv;
    varying vec3 vPosition;
    
    void main() {
      vUv = uv;
      vPosition = position;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  
  fragmentShader: `
    uniform float time;
    uniform vec3 color1;
    uniform vec3 color2;
    
    varying vec2 vUv;
    varying vec3 vPosition;
    
    void main() {
      // 渐变效果
      float gradient = sin(vUv.y * 3.14159 + time) * 0.5 + 0.5;
      vec3 color = mix(color1, color2, gradient);
      
      gl_FragColor = vec4(color, 1.0);
    }
  `,
  
  side: THREE.DoubleSide
});

// 更新时间uniform
function updateShader(delta: number) {
  customShaderMaterial.uniforms.time.value += delta;
}

11.2.2 后处理着色器

import { EffectComposer, ShaderPass } from 'three/examples/jsm/postprocessing';

// 自定义后处理效果
const CustomEffect = {
  uniforms: {
    tDiffuse: { value: null },
    amount: { value: 1.0 },
    time: { value: 0 }
  },
  
  vertexShader: `
    varying vec2 vUv;
    
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  
  fragmentShader: `
    uniform sampler2D tDiffuse;
    uniform float amount;
    uniform float time;
    
    varying vec2 vUv;
    
    void main() {
      vec4 color = texture2D(tDiffuse, vUv);
      
      // 色相偏移效果
      float hueShift = sin(time) * amount;
      
      // RGB到HSV
      float maxC = max(color.r, max(color.g, color.b));
      float minC = min(color.r, min(color.g, color.b));
      float delta = maxC - minC;
      
      float h = 0.0;
      if (delta > 0.0) {
        if (maxC == color.r) {
          h = mod((color.g - color.b) / delta, 6.0);
        } else if (maxC == color.g) {
          h = (color.b - color.r) / delta + 2.0;
        } else {
          h = (color.r - color.g) / delta + 4.0;
        }
        h /= 6.0;
      }
      
      // 应用色相偏移
      h = mod(h + hueShift, 1.0);
      
      // HSV到RGB
      float s = delta / maxC;
      float v = maxC;
      
      float c = v * s;
      float x = c * (1.0 - abs(mod(h * 6.0, 2.0) - 1.0));
      float m = v - c;
      
      vec3 rgb;
      if (h < 1.0/6.0) rgb = vec3(c, x, 0.0);
      else if (h < 2.0/6.0) rgb = vec3(x, c, 0.0);
      else if (h < 3.0/6.0) rgb = vec3(0.0, c, x);
      else if (h < 4.0/6.0) rgb = vec3(0.0, x, c);
      else if (h < 5.0/6.0) rgb = vec3(x, 0.0, c);
      else rgb = vec3(c, 0.0, x);
      
      gl_FragColor = vec4(rgb + m, color.a);
    }
  `
};

// 添加到效果合成器
const composer = new EffectComposer(renderer);
const customPass = new ShaderPass(CustomEffect);
composer.addPass(customPass);

11.2.3 实例化着色器

// 实例化渲染着色器
const instancedMaterial = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 }
  },
  
  vertexShader: `
    attribute vec3 instancePosition;
    attribute vec3 instanceColor;
    attribute float instanceScale;
    
    varying vec3 vColor;
    
    void main() {
      vColor = instanceColor;
      
      vec3 pos = position * instanceScale + instancePosition;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  
  fragmentShader: `
    varying vec3 vColor;
    
    void main() {
      gl_FragColor = vec4(vColor, 1.0);
    }
  `
});

// 创建实例化几何体
function createInstancedMesh(geometry: THREE.BufferGeometry, count: number) {
  const positions = new Float32Array(count * 3);
  const colors = new Float32Array(count * 3);
  const scales = new Float32Array(count);
  
  for (let i = 0; i < count; i++) {
    positions[i * 3] = (Math.random() - 0.5) * 100;
    positions[i * 3 + 1] = (Math.random() - 0.5) * 100;
    positions[i * 3 + 2] = (Math.random() - 0.5) * 100;
    
    colors[i * 3] = Math.random();
    colors[i * 3 + 1] = Math.random();
    colors[i * 3 + 2] = Math.random();
    
    scales[i] = Math.random() * 0.5 + 0.5;
  }
  
  geometry.setAttribute('instancePosition', new THREE.InstancedBufferAttribute(positions, 3));
  geometry.setAttribute('instanceColor', new THREE.InstancedBufferAttribute(colors, 3));
  geometry.setAttribute('instanceScale', new THREE.InstancedBufferAttribute(scales, 1));
  
  return new THREE.InstancedMesh(geometry, instancedMaterial, count);
}

11.3 性能优化

11.3.1 渲染优化

// 性能监控
import Stats from 'three/examples/jsm/libs/stats.module';

const stats = new Stats();
document.body.appendChild(stats.dom);

// 渲染循环中更新
function animate() {
  stats.begin();
  
  // 渲染
  renderer.render(scene, camera);
  
  stats.end();
  requestAnimationFrame(animate);
}

// 视锥裁剪优化
scene.traverse((obj) => {
  if (obj instanceof THREE.Mesh) {
    obj.frustumCulled = true;
  }
});

// LOD设置
function createLOD(meshes: THREE.Mesh[]) {
  const lod = new THREE.LOD();
  
  meshes.forEach((mesh, i) => {
    lod.addLevel(mesh, i * 50);
  });
  
  return lod;
}

// 合并几何体
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';

function mergeStaticMeshes(meshes: THREE.Mesh[]) {
  const geometries = meshes.map(m => m.geometry);
  const merged = mergeGeometries(geometries);
  return new THREE.Mesh(merged, meshes[0].material);
}

11.3.2 内存优化

// 资源清理
function disposeObject(object: THREE.Object3D) {
  object.traverse((child) => {
    if (child instanceof THREE.Mesh) {
      // 清理几何体
      child.geometry.dispose();
      
      // 清理材质
      if (Array.isArray(child.material)) {
        child.material.forEach(mat => disposeMaterial(mat));
      } else {
        disposeMaterial(child.material);
      }
    }
  });
  
  // 从父节点移除
  object.parent?.remove(object);
}

function disposeMaterial(material: THREE.Material) {
  // 清理贴图
  for (const key of Object.keys(material)) {
    const value = (material as any)[key];
    if (value instanceof THREE.Texture) {
      value.dispose();
    }
  }
  material.dispose();
}

// 贴图缓存管理
class TextureCache {
  private cache = new Map<string, THREE.Texture>();
  private maxSize = 100;
  
  get(url: string): THREE.Texture | undefined {
    return this.cache.get(url);
  }
  
  set(url: string, texture: THREE.Texture) {
    if (this.cache.size >= this.maxSize) {
      // 移除最旧的
      const firstKey = this.cache.keys().next().value;
      const oldTexture = this.cache.get(firstKey);
      oldTexture?.dispose();
      this.cache.delete(firstKey);
    }
    this.cache.set(url, texture);
  }
  
  clear() {
    this.cache.forEach(tex => tex.dispose());
    this.cache.clear();
  }
}

11.3.3 加载优化

// 渐进式加载
class ProgressiveLoader {
  private queue: Array<{ url: string; priority: number; callback: Function }> = [];
  private loading = 0;
  private maxConcurrent = 4;
  
  add(url: string, priority: number, callback: Function) {
    this.queue.push({ url, priority, callback });
    this.queue.sort((a, b) => b.priority - a.priority);
    this.processQueue();
  }
  
  private async processQueue() {
    while (this.loading < this.maxConcurrent && this.queue.length > 0) {
      const item = this.queue.shift();
      if (item) {
        this.loading++;
        try {
          const result = await this.loadItem(item.url);
          item.callback(null, result);
        } catch (error) {
          item.callback(error, null);
        }
        this.loading--;
        this.processQueue();
      }
    }
  }
  
  private async loadItem(url: string) {
    // 加载逻辑
    return await viewer.loader.load(url);
  }
}

// 基于距离的加载
class DistanceBasedLoader {
  private objects = new Map<string, { position: THREE.Vector3; loaded: boolean }>();
  private loadDistance = 100;
  private unloadDistance = 150;
  
  register(id: string, position: THREE.Vector3) {
    this.objects.set(id, { position, loaded: false });
  }
  
  update(cameraPosition: THREE.Vector3) {
    this.objects.forEach((obj, id) => {
      const distance = cameraPosition.distanceTo(obj.position);
      
      if (!obj.loaded && distance < this.loadDistance) {
        this.loadObject(id);
        obj.loaded = true;
      } else if (obj.loaded && distance > this.unloadDistance) {
        this.unloadObject(id);
        obj.loaded = false;
      }
    });
  }
  
  private async loadObject(id: string) {
    // 加载对象
  }
  
  private unloadObject(id: string) {
    // 卸载对象
  }
}

11.4 WebWorker集成

11.4.1 Worker基础

// worker.ts
self.onmessage = async (event) => {
  const { type, data } = event.data;
  
  switch (type) {
    case 'processGeometry':
      const result = processGeometry(data);
      self.postMessage({ type: 'geometryProcessed', data: result });
      break;
      
    case 'calculatePhysics':
      const physicsResult = calculatePhysics(data);
      self.postMessage({ type: 'physicsCalculated', data: physicsResult });
      break;
  }
};

function processGeometry(vertices: Float32Array) {
  // 在Worker中处理几何体计算
  const processed = new Float32Array(vertices.length);
  
  for (let i = 0; i < vertices.length; i += 3) {
    // 示例:简化顶点处理
    processed[i] = Math.round(vertices[i] * 100) / 100;
    processed[i + 1] = Math.round(vertices[i + 1] * 100) / 100;
    processed[i + 2] = Math.round(vertices[i + 2] * 100) / 100;
  }
  
  return processed;
}

function calculatePhysics(bodies: any[]) {
  // 物理计算
  return bodies.map(body => ({
    id: body.id,
    position: [
      body.position[0] + body.velocity[0],
      body.position[1] + body.velocity[1],
      body.position[2] + body.velocity[2]
    ]
  }));
}

11.4.2 Worker管理器

// WorkerManager.ts
class WorkerManager {
  private workers: Worker[] = [];
  private taskQueue: Array<{ task: any; resolve: Function; reject: Function }> = [];
  private busyWorkers = new Set<Worker>();
  
  constructor(workerUrl: string, count = navigator.hardwareConcurrency || 4) {
    for (let i = 0; i < count; i++) {
      const worker = new Worker(workerUrl, { type: 'module' });
      worker.onmessage = (event) => this.onWorkerMessage(worker, event);
      worker.onerror = (error) => this.onWorkerError(worker, error);
      this.workers.push(worker);
    }
  }
  
  async runTask<T>(type: string, data: any): Promise<T> {
    return new Promise((resolve, reject) => {
      this.taskQueue.push({ task: { type, data }, resolve, reject });
      this.processQueue();
    });
  }
  
  private processQueue() {
    for (const worker of this.workers) {
      if (!this.busyWorkers.has(worker) && this.taskQueue.length > 0) {
        const { task, resolve, reject } = this.taskQueue.shift()!;
        this.busyWorkers.add(worker);
        
        // 保存回调
        (worker as any)._resolve = resolve;
        (worker as any)._reject = reject;
        
        worker.postMessage(task);
      }
    }
  }
  
  private onWorkerMessage(worker: Worker, event: MessageEvent) {
    this.busyWorkers.delete(worker);
    const resolve = (worker as any)._resolve;
    if (resolve) {
      resolve(event.data);
    }
    this.processQueue();
  }
  
  private onWorkerError(worker: Worker, error: ErrorEvent) {
    this.busyWorkers.delete(worker);
    const reject = (worker as any)._reject;
    if (reject) {
      reject(error);
    }
    this.processQueue();
  }
  
  terminate() {
    this.workers.forEach(worker => worker.terminate());
    this.workers = [];
  }
}

// 使用
const workerManager = new WorkerManager('/workers/geometry-worker.js');

const processedData = await workerManager.runTask<Float32Array>(
  'processGeometry',
  geometryData
);

11.5 数据可视化

11.5.1 热力图

// 热力图生成
class HeatmapGenerator {
  private width: number;
  private height: number;
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  
  constructor(width = 256, height = 256) {
    this.width = width;
    this.height = height;
    this.canvas = document.createElement('canvas');
    this.canvas.width = width;
    this.canvas.height = height;
    this.ctx = this.canvas.getContext('2d')!;
  }
  
  generate(points: Array<{ x: number; y: number; value: number }>) {
    // 清空画布
    this.ctx.clearRect(0, 0, this.width, this.height);
    
    // 绘制热点
    points.forEach(point => {
      const gradient = this.ctx.createRadialGradient(
        point.x * this.width,
        point.y * this.height,
        0,
        point.x * this.width,
        point.y * this.height,
        point.value * 50
      );
      gradient.addColorStop(0, 'rgba(255, 0, 0, 1)');
      gradient.addColorStop(1, 'rgba(255, 0, 0, 0)');
      
      this.ctx.fillStyle = gradient;
      this.ctx.fillRect(0, 0, this.width, this.height);
    });
    
    // 应用颜色映射
    this.applyColorMap();
    
    // 创建纹理
    const texture = new THREE.CanvasTexture(this.canvas);
    return texture;
  }
  
  private applyColorMap() {
    const imageData = this.ctx.getImageData(0, 0, this.width, this.height);
    const data = imageData.data;
    
    for (let i = 0; i < data.length; i += 4) {
      const value = data[i] / 255;
      const color = this.valueToColor(value);
      
      data[i] = color.r;
      data[i + 1] = color.g;
      data[i + 2] = color.b;
    }
    
    this.ctx.putImageData(imageData, 0, 0);
  }
  
  private valueToColor(value: number) {
    // 蓝 -> 绿 -> 黄 -> 红
    if (value < 0.25) {
      return { r: 0, g: Math.floor(value * 4 * 255), b: 255 };
    } else if (value < 0.5) {
      return { r: 0, g: 255, b: Math.floor((0.5 - value) * 4 * 255) };
    } else if (value < 0.75) {
      return { r: Math.floor((value - 0.5) * 4 * 255), g: 255, b: 0 };
    } else {
      return { r: 255, g: Math.floor((1 - value) * 4 * 255), b: 0 };
    }
  }
}

11.5.2 数据图表集成

// ECharts集成
import * as echarts from 'echarts';

class ChartPanel {
  private chart: echarts.ECharts;
  private texture: THREE.CanvasTexture;
  
  constructor(container: HTMLElement) {
    this.chart = echarts.init(container);
    this.texture = new THREE.CanvasTexture(this.chart.getDom() as HTMLCanvasElement);
  }
  
  setOption(option: echarts.EChartsOption) {
    this.chart.setOption(option);
    this.texture.needsUpdate = true;
  }
  
  getTexture(): THREE.CanvasTexture {
    return this.texture;
  }
  
  update() {
    this.texture.needsUpdate = true;
  }
  
  dispose() {
    this.chart.dispose();
    this.texture.dispose();
  }
}

// 创建3D图表
function create3DChart(data: any[]) {
  const container = document.createElement('div');
  container.style.width = '512px';
  container.style.height = '512px';
  container.style.position = 'absolute';
  container.style.left = '-9999px';
  document.body.appendChild(container);
  
  const chartPanel = new ChartPanel(container);
  
  chartPanel.setOption({
    title: { text: '数据统计' },
    xAxis: { type: 'category', data: data.map(d => d.name) },
    yAxis: { type: 'value' },
    series: [{
      type: 'bar',
      data: data.map(d => d.value)
    }]
  });
  
  // 创建3D平面显示图表
  const geometry = new THREE.PlaneGeometry(10, 10);
  const material = new THREE.MeshBasicMaterial({
    map: chartPanel.getTexture(),
    side: THREE.DoubleSide
  });
  
  const mesh = new THREE.Mesh(geometry, material);
  
  return { mesh, chartPanel };
}

11.6 网络与协作

11.6.1 WebSocket集成

// 实时协作客户端
class CollaborationClient {
  private ws: WebSocket;
  private userId: string;
  private callbacks = new Map<string, Function[]>();
  
  constructor(serverUrl: string) {
    this.userId = this.generateUserId();
    this.ws = new WebSocket(serverUrl);
    
    this.ws.onopen = () => this.onOpen();
    this.ws.onmessage = (event) => this.onMessage(event);
    this.ws.onclose = () => this.onClose();
    this.ws.onerror = (error) => this.onError(error);
  }
  
  private generateUserId(): string {
    return Math.random().toString(36).substring(2);
  }
  
  private onOpen() {
    console.log('连接已建立');
    this.send('join', { userId: this.userId });
  }
  
  private onMessage(event: MessageEvent) {
    const { type, data, userId } = JSON.parse(event.data);
    
    if (userId === this.userId) return; // 忽略自己的消息
    
    const handlers = this.callbacks.get(type) || [];
    handlers.forEach(handler => handler(data, userId));
  }
  
  private onClose() {
    console.log('连接已关闭');
  }
  
  private onError(error: Event) {
    console.error('WebSocket错误:', error);
  }
  
  send(type: string, data: any) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type, data, userId: this.userId }));
    }
  }
  
  on(type: string, callback: Function) {
    if (!this.callbacks.has(type)) {
      this.callbacks.set(type, []);
    }
    this.callbacks.get(type)!.push(callback);
  }
  
  off(type: string, callback: Function) {
    const handlers = this.callbacks.get(type);
    if (handlers) {
      const index = handlers.indexOf(callback);
      if (index > -1) handlers.splice(index, 1);
    }
  }
  
  // 同步场景操作
  syncObjectTransform(objectId: string, transform: any) {
    this.send('transform', { objectId, transform });
  }
  
  syncObjectAdd(objectData: any) {
    this.send('addObject', objectData);
  }
  
  syncObjectRemove(objectId: string) {
    this.send('removeObject', { objectId });
  }
}

// 使用
const collab = new CollaborationClient('wss://example.com/collab');

collab.on('transform', (data, userId) => {
  const object = scene.getObjectByProperty('uuid', data.objectId);
  if (object) {
    object.position.fromArray(data.transform.position);
    object.rotation.fromArray(data.transform.rotation);
    object.scale.fromArray(data.transform.scale);
  }
});

// 当本地对象变换时同步
viewer.on('objectTransformed', (object) => {
  collab.syncObjectTransform(object.uuid, {
    position: object.position.toArray(),
    rotation: object.rotation.toArray(),
    scale: object.scale.toArray()
  });
});

11.7 本章小结

本章介绍了Astral3D的二次开发进阶内容,主要包括:

  1. 自定义加载器:加载器架构和创建自定义格式加载器
  2. 自定义着色器:着色器材质、后处理效果、实例化渲染
  3. 性能优化:渲染优化、内存管理、加载优化
  4. WebWorker集成:将计算密集型任务移至Worker线程
  5. 数据可视化:热力图生成、图表集成
  6. 网络协作:WebSocket实时协作

通过本章的学习,读者应该能够进行Astral3D的深度定制开发,构建复杂的3D应用。


下一章预告:第十二章将介绍Astral3D的实战案例与最佳实践,包括数字孪生应用、BIM可视化平台、工业监控系统等完整案例。


posted @ 2026-01-10 13:17  我才是银古  阅读(8)  评论(0)    收藏  举报