第38节:WebGL 2.0与Three.js新特性

第38节:WebGL 2.0与Three.js新特性

概述

WebGL 2.0是现代浏览器中3D图形技术的重大飞跃,引入了计算着色器、变换反馈、多重渲染目标等高级特性。本节将深入探索WebGL 2.0的核心功能,以及Three.js如何集成这些新特性来提升渲染性能和质量。

WebGL 2.0特性架构:
WebGL 2.0 核心特性 着色器增强 纹理系统 缓冲区操作 渲染管线 计算着色器 变换反馈 几何着色器 3D纹理 数组纹理 压缩纹理 统一缓冲区 顶点数组对象 实例化数组 多重渲染目标 查询对象 采样器对象 GPU计算 体积渲染 性能优化 高级效果

核心原理深度解析

WebGL 2.0 vs WebGL 1.0 特性对比

特性 WebGL 1.0 WebGL 2.0 改进意义
着色器版本 GLSL ES 1.0 GLSL ES 3.0 更丰富的语法和功能
纹理格式 有限制 支持3D纹理、数组纹理 体积渲染、纹理数组
缓冲区操作 基础功能 统一缓冲区、变换反馈 性能大幅提升
多重渲染目标 扩展 原生支持 延迟渲染、后处理
实例化渲染 扩展 原生支持 大规模对象渲染
计算着色器 不支持 WebGL2 Compute GPU通用计算

Three.js对WebGL 2.0的集成策略

Three.js通过渐进增强的方式支持WebGL 2.0:

javascript 复制代码
// 检测WebGL 2.0支持
const isWebGL2Supported = () => {
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl2');
  return gl !== null && gl instanceof WebGL2RenderingContext;
};

// Three.js中的WebGL 2.0渲染器
const renderer = new THREE.WebGLRenderer({
  context: gl, // 可传入现有的WebGL2上下文
  powerPreference: "high-performance"
});

完整代码实现

WebGL 2.0高级特性演示

vue 复制代码
<template>
  <div class="webgl2-features-container">
    <!-- 主渲染画布 -->
    <canvas ref="webgl2Canvas" class="webgl2-canvas"></canvas>
    
    <!-- 特性控制面板 -->
    <div class="features-controls">
      <div class="control-section">
        <h3>🚀 WebGL 2.0 特性</h3>
        
        <div class="webgl2-status">
          <div class="status-item">
            <span>WebGL 2.0 支持:</span>
            <span :class="webgl2Supported ? 'status-supported' : 'status-unsupported'">
              {{ webgl2Supported ? '✅ 已支持' : '❌ 不支持' }}
            </span>
          </div>
          <div class="status-item">
            <span>渲染器类型:</span>
            <span>{{ rendererType }}</span>
          </div>
          <div class="status-item">
            <span>GPU 信息:</span>
            <span>{{ gpuInfo }}</span>
          </div>
        </div>
        
        <div class="feature-presets">
          <button 
            v-for="preset in featurePresets" 
            :key="preset.id"
            @click="loadFeaturePreset(preset)"
            class="preset-button"
            :class="{ active: currentPreset?.id === preset.id }"
          >
            {{ preset.name }}
          </button>
        </div>
      </div>

      <div class="control-section">
        <h3>🔄 计算着色器</h3>
        
        <div class="compute-shader-controls">
          <div class="control-group">
            <label>粒子数量: {{ formatNumber(particleCount) }}</label>
            <input 
              type="range" 
              v-model="particleCount" 
              min="1000" 
              max="1000000" 
              step="1000"
              :disabled="!computeShaderEnabled"
            >
          </div>
          
          <div class="control-group">
            <label>模拟速度: {{ simulationSpeed }}</label>
            <input 
              type="range" 
              v-model="simulationSpeed" 
              min="0.1" 
              max="5" 
              step="0.1"
              :disabled="!computeShaderEnabled"
            >
          </div>
          
          <div class="control-group">
            <label>物理精度: {{ physicsPrecision }}</label>
            <input 
              type="range" 
              v-model="physicsPrecision" 
              min="0.5" 
              max="2" 
              step="0.1"
              :disabled="!computeShaderEnabled"
            >
          </div>
        </div>
        
        <div class="compute-options">
          <div class="control-group">
            <label>启用计算着色器</label>
            <input type="checkbox" v-model="computeShaderEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>启用变换反馈</label>
            <input type="checkbox" v-model="transformFeedbackEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>实时物理模拟</label>
            <input type="checkbox" v-model="realtimePhysicsEnabled" :disabled="!computeShaderEnabled">
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>🎨 多重渲染目标</h3>
        
        <div class="mrt-controls">
          <div class="control-group">
            <label>MRT 缓冲区数量: {{ mrtBufferCount }}</label>
            <input 
              type="range" 
              v-model="mrtBufferCount" 
              min="2" 
              max="8" 
              step="1"
              :disabled="!mrtEnabled"
            >
          </div>
          
          <div class="control-group">
            <label>G-Buffer 分辨率: {{ gBufferResolution }}%</label>
            <input 
              type="range" 
              v-model="gBufferResolution" 
              min="25" 
              max="100" 
              step="5"
              :disabled="!mrtEnabled"
            >
          </div>
        </div>
        
        <div class="mrt-options">
          <div class="control-group">
            <label>启用 MRT</label>
            <input type="checkbox" v-model="mrtEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>延迟渲染</label>
            <input type="checkbox" v-model="deferredRenderingEnabled" :disabled="!mrtEnabled">
          </div>
          
          <div class="control-group">
            <label>HDR 渲染</label>
            <input type="checkbox" v-model="hdrRenderingEnabled">
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>📊 实例化渲染</h3>
        
        <div class="instancing-controls">
          <div class="control-group">
            <label>实例数量: {{ formatNumber(instanceCount) }}</label>
            <input 
              type="range" 
              v-model="instanceCount" 
              min="100" 
              max="100000" 
              step="100"
              :disabled="!instancingEnabled"
            >
          </div>
          
          <div class="control-group">
            <label>LOD 级别: {{ instancingLOD }}</label>
            <input 
              type="range" 
              v-model="instancingLOD" 
              min="1" 
              max="4" 
              step="1"
              :disabled="!instancingEnabled"
            >
          </div>
        </div>
        
        <div class="instancing-options">
          <div class="control-group">
            <label>启用实例化</label>
            <input type="checkbox" v-model="instancingEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>动态实例更新</label>
            <input type="checkbox" v-model="dynamicInstancingEnabled" :disabled="!instancingEnabled">
          </div>
          
          <div class="control-group">
            <label>视锥体剔除</label>
            <input type="checkbox" v-model="frustumCullingEnabled">
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>🔧 高级纹理</h3>
        
        <div class="texture-controls">
          <div class="control-group">
            <label>3D 纹理分辨率: {{ texture3DResolution }}</label>
            <input 
              type="range" 
              v-model="texture3DResolution" 
              min="32" 
              max="256" 
              step="32"
              :disabled="!texture3DEnabled"
            >
          </div>
          
          <div class="control-group">
            <label>数组纹理层数: {{ arrayTextureLayers }}</label>
            <input 
              type="range" 
              v-model="arrayTextureLayers" 
              min="2" 
              max="16" 
              step="2"
              :disabled="!arrayTexturesEnabled"
            >
          </div>
        </div>
        
        <div class="texture-options">
          <div class="control-group">
            <label>启用 3D 纹理</label>
            <input type="checkbox" v-model="texture3DEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>启用数组纹理</label>
            <input type="checkbox" v-model="arrayTexturesEnabled" :disabled="!webgl2Supported">
          </div>
          
          <div class="control-group">
            <label>纹理压缩</label>
            <input type="checkbox" v-model="textureCompressionEnabled">
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>📈 性能监控</h3>
        <div class="performance-stats">
          <div class="stat-item">
            <span>绘制调用:</span>
            <span>{{ drawCalls }}</span>
          </div>
          <div class="stat-item">
            <span>实例绘制:</span>
            <span>{{ instanceDraws }}</span>
          </div>
          <div class="stat-item">
            <span>计算时间:</span>
            <span>{{ computeTime.toFixed(2) }}ms</span>
          </div>
          <div class="stat-item">
            <span>帧率:</span>
            <span>{{ currentFPS }} FPS</span>
          </div>
          <div class="stat-item">
            <span>GPU 内存:</span>
            <span>{{ formatMemory(gpuMemory) }}</span>
          </div>
          <div class="stat-item">
            <span>缓冲区数量:</span>
            <span>{{ bufferCount }}</span>
          </div>
        </div>
      </div>
    </div>

    <!-- 调试视图切换 -->
    <div class="debug-views">
      <button 
        v-for="view in debugViews" 
        :key="view.id"
        @click="setDebugView(view)"
        class="debug-button"
        :class="{ active: currentDebugView?.id === view.id }"
      >
        {{ view.name }}
      </button>
    </div>

    <!-- 特性信息显示 -->
    <div class="features-info">
      <div class="info-panel">
        <h4>活动特性</h4>
        <div class="active-features">
          <div 
            v-for="feature in activeFeatures" 
            :key="feature"
            class="feature-tag"
          >
            {{ feature }}
          </div>
        </div>
      </div>
    </div>

    <!-- WebGL 2.0状态指示器 -->
    <div class="webgl2-indicator" :class="webgl2Supported ? 'supported' : 'unsupported'">
      <div class="indicator-dot"></div>
      <span>WebGL 2.0</span>
    </div>

    <!-- 加载界面 -->
    <div v-if="isLoading" class="loading-overlay">
      <div class="loading-content">
        <div class="webgl2-loader">
          <div class="gpu-chip">
            <div class="chip-core"></div>
            <div class="chip-core"></div>
            <div class="chip-core"></div>
            <div class="chip-core"></div>
          </div>
        </div>
        <h3>初始化 WebGL 2.0 系统...</h3>
        <div class="loading-progress">
          <div class="progress-bar">
            <div class="progress-fill" :style="loadingProgressStyle"></div>
          </div>
          <span>{{ loadingMessage }}</span>
        </div>
        <div class="feature-loading">
          <div class="feature-item" v-for="item in loadingFeatures" :key="item.name">
            <span>{{ item.name }}</span>
            <span :class="item.status">{{ item.statusText }}</span>
          </div>
        </div>
      </div>
    </div>

    <!-- 不支持WebGL 2.0的提示 -->
    <div v-if="!webgl2Supported && !isLoading" class="unsupported-overlay">
      <div class="unsupported-content">
        <div class="warning-icon">⚠️</div>
        <h3>WebGL 2.0 不支持</h3>
        <p>您的浏览器或设备不支持 WebGL 2.0,部分高级功能将不可用。</p>
        <div class="fallback-options">
          <button @click="enableWebGL1Fallback" class="fallback-button">
            启用 WebGL 1.0 回退
          </button>
          <button @click="showCompatibilityInfo" class="info-button">
            兼容性信息
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
import * as THREE from 'three';

// WebGL 2.0 计算着色器系统
class WebGL2ComputeSystem {
  constructor(renderer) {
    this.renderer = renderer;
    this.gl = renderer.getContext();
    this.computeShader = null;
    this.transformFeedback = null;
    this.buffers = new Map();
    this.isSupported = this.checkComputeSupport();
  }

  // 检查计算着色器支持
  checkComputeSupport() {
    // 检查WebGL 2.0 Compute扩展
    const extensions = this.gl.getSupportedExtensions();
    return extensions.includes('WEBGL_compute_shader') || 
           extensions.includes('WEBGL_parallel_shader_compile');
  }

  // 创建计算着色器
  createComputeShader(computeSource, transformVaryings = []) {
    if (!this.isSupported) {
      console.warn('计算着色器不支持');
      return null;
    }

    const computeShader = this.gl.createShader(this.gl.COMPUTE_SHADER);
    this.gl.shaderSource(computeShader, computeSource);
    this.gl.compileShader(computeShader);

    if (!this.gl.getShaderParameter(computeShader, this.gl.COMPILE_STATUS)) {
      console.error('计算着色器编译错误:', this.gl.getShaderInfoLog(computeShader));
      this.gl.deleteShader(computeShader);
      return null;
    }

    const program = this.gl.createProgram();
    this.gl.attachShader(program, computeShader);
    
    // 设置变换反馈输出
    if (transformVaryings.length > 0) {
      this.gl.transformFeedbackVaryings(
        program, 
        transformVaryings, 
        this.gl.INTERLEAVED_ATTRIBS
      );
    }
    
    this.gl.linkProgram(program);

    if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
      console.error('计算程序链接错误:', this.gl.getProgramInfoLog(program));
      this.gl.deleteProgram(program);
      return null;
    }

    this.computeShader = program;
    return program;
  }

  // 创建存储缓冲区
  createStorageBuffer(data, usage = this.gl.DYNAMIC_COPY) {
    const buffer = this.gl.createBuffer();
    this.gl.bindBuffer(this.gl.SHADER_STORAGE_BUFFER, buffer);
    this.gl.bufferData(this.gl.SHADER_STORAGE_BUFFER, data, usage);
    this.gl.bindBuffer(this.gl.SHADER_STORAGE_BUFFER, null);
    return buffer;
  }

  // 绑定存储缓冲区
  bindStorageBuffer(buffer, bindingPoint) {
    this.gl.bindBufferBase(this.gl.SHADER_STORAGE_BUFFER, bindingPoint, buffer);
  }

  // 执行计算着色器
  dispatchCompute(groupsX, groupsY = 1, groupsZ = 1) {
    if (!this.computeShader || !this.isSupported) return;

    this.gl.useProgram(this.computeShader);
    this.gl.dispatchCompute(groupsX, groupsY, groupsZ);
    this.gl.memoryBarrier(this.gl.SHADER_STORAGE_BARRIER_BIT);
  }

  // 创建变换反馈
  createTransformFeedback() {
    this.transformFeedback = this.gl.createTransformFeedback();
    return this.transformFeedback;
  }

  // 绑定变换反馈缓冲区
  bindTransformFeedbackBuffer(buffer, index = 0) {
    if (!this.transformFeedback) return;
    this.gl.bindTransformFeedback(this.gl.TRANSFORM_FEEDBACK, this.transformFeedback);
    this.gl.bindBufferBase(this.gl.TRANSFORM_FEEDBACK_BUFFER, index, buffer);
  }

  // 开始变换反馈
  beginTransformFeedback(primitiveMode = this.gl.POINTS) {
    this.gl.beginTransformFeedback(primitiveMode);
  }

  // 结束变换反馈
  endTransformFeedback() {
    this.gl.endTransformFeedback();
    this.gl.bindTransformFeedback(this.gl.TRANSFORM_FEEDBACK, null);
  }
}

// 多重渲染目标系统
class MRTSystem {
  constructor(renderer, width, height, numTargets = 4) {
    this.renderer = renderer;
    this.gl = renderer.getContext();
    this.width = width;
    this.height = height;
    this.numTargets = numTargets;
    this.renderTargets = [];
    this.textures = [];
    
    this.setupMRT();
  }

  // 设置多重渲染目标
  setupMRT() {
    // 创建渲染目标
    this.renderTarget = new THREE.WebGLMultipleRenderTargets(this.width, this.height, this.numTargets);
    
    // 配置每个渲染目标的纹理
    for (let i = 0; i < this.numTargets; i++) {
      const texture = this.renderTarget.texture[i];
      
      // 设置纹理参数
      texture.minFilter = THREE.NearestFilter;
      texture.magFilter = THREE.NearestFilter;
      texture.format = THREE.RGBAFormat;
      texture.type = THREE.FloatType;
      texture.generateMipmaps = false;
      
      this.textures.push(texture);
    }
    
    // 设置深度纹理
    this.renderTarget.depthTexture = new THREE.DepthTexture(this.width, this.height);
    this.renderTarget.depthTexture.format = THREE.DepthFormat;
    this.renderTarget.depthTexture.type = THREE.UnsignedIntType;
  }

  // 调整大小
  resize(width, height) {
    this.width = width;
    this.height = height;
    this.renderTarget.setSize(width, height);
  }

  // 获取纹理
  getTexture(index) {
    return this.textures[index];
  }

  // 获取深度纹理
  getDepthTexture() {
    return this.renderTarget.depthTexture;
  }

  // 绑定渲染目标
  bind() {
    this.renderer.setRenderTarget(this.renderTarget);
  }

  // 解绑渲染目标
  unbind() {
    this.renderer.setRenderTarget(null);
  }

  // 清理资源
  dispose() {
    this.renderTarget.dispose();
  }
}

// 实例化渲染系统
class InstancingSystem {
  constructor() {
    this.instancedMeshes = new Map();
    this.instanceData = new Map();
    this.maxInstances = 100000;
  }

  // 创建实例化网格
  createInstancedMesh(geometry, material, count) {
    const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
    instancedMesh.count = Math.min(count, this.maxInstances);
    
    // 存储实例数据
    this.instanceData.set(instancedMesh.uuid, {
      positions: new Float32Array(count * 3),
      rotations: new Float32Array(count * 4),
      scales: new Float32Array(count * 3),
      colors: new Float32Array(count * 3)
    });
    
    this.instancedMeshes.set(instancedMesh.uuid, instancedMesh);
    return instancedMesh;
  }

  // 更新实例数据
  updateInstanceData(meshId, instanceIndex, data) {
    const instanceData = this.instanceData.get(meshId);
    if (!instanceData) return;

    const { position, rotation, scale, color } = data;
    const idx = instanceIndex * 3;
    const ridx = instanceIndex * 4;

    // 更新位置
    if (position) {
      instanceData.positions[idx] = position.x;
      instanceData.positions[idx + 1] = position.y;
      instanceData.positions[idx + 2] = position.z;
    }

    // 更新旋转(四元数)
    if (rotation) {
      instanceData.rotations[ridx] = rotation.x;
      instanceData.rotations[ridx + 1] = rotation.y;
      instanceData.rotations[ridx + 2] = rotation.z;
      instanceData.rotations[ridx + 3] = rotation.w;
    }

    // 更新缩放
    if (scale) {
      instanceData.scales[idx] = scale.x;
      instanceData.scales[idx + 1] = scale.y;
      instanceData.scales[idx + 2] = scale.z;
    }

    // 更新颜色
    if (color) {
      instanceData.colors[idx] = color.r;
      instanceData.colors[idx + 1] = color.g;
      instanceData.colors[idx + 2] = color.b;
    }
  }

  // 应用实例数据到GPU
  applyInstanceData(meshId) {
    const mesh = this.instancedMeshes.get(meshId);
    const data = this.instanceData.get(meshId);
    if (!mesh || !data) return;

    const matrix = new THREE.Matrix4();
    const position = new THREE.Vector3();
    const quaternion = new THREE.Quaternion();
    const scale = new THREE.Vector3();
    const color = new THREE.Color();

    for (let i = 0; i < mesh.count; i++) {
      // 设置位置、旋转、缩放
      position.fromArray(data.positions, i * 3);
      quaternion.fromArray(data.rotations, i * 4);
      scale.fromArray(data.scales, i * 3);
      
      matrix.compose(position, quaternion, scale);
      mesh.setMatrixAt(i, matrix);

      // 设置颜色
      color.fromArray(data.colors, i * 3);
      mesh.setColorAt(i, color);
    }

    mesh.instanceMatrix.needsUpdate = true;
    if (mesh.instanceColor) {
      mesh.instanceColor.needsUpdate = true;
    }
  }

  // 动态更新实例(使用计算着色器)
  updateInstancesDynamic(meshId, computeSystem) {
    // 使用计算着色器批量更新实例数据
    const mesh = this.instancedMeshes.get(meshId);
    const data = this.instanceData.get(meshId);
    if (!mesh || !data || !computeSystem.isSupported) return;

    // 创建存储缓冲区
    const positionBuffer = computeSystem.createStorageBuffer(data.positions);
    const rotationBuffer = computeSystem.createStorageBuffer(data.rotations);
    
    // 绑定缓冲区
    computeSystem.bindStorageBuffer(positionBuffer, 0);
    computeSystem.bindStorageBuffer(rotationBuffer, 1);

    // 执行计算着色器
    const groupSize = Math.ceil(mesh.count / 64);
    computeSystem.dispatchCompute(groupSize);

    // 应用更新后的数据
    this.applyInstanceData(meshId);
  }
}

// 3D纹理系统
class Texture3DSystem {
  constructor(renderer) {
    this.renderer = renderer;
    this.gl = renderer.getContext();
    this.textures = new Map();
  }

  // 创建3D纹理
  create3DTexture(width, height, depth, data = null) {
    const texture = this.gl.createTexture();
    this.gl.bindTexture(this.gl.TEXTURE_3D, texture);

    // 设置纹理参数
    this.gl.texParameteri(this.gl.TEXTURE_3D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
    this.gl.texParameteri(this.gl.TEXTURE_3D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
    this.gl.texParameteri(this.gl.TEXTURE_3D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
    this.gl.texParameteri(this.gl.TEXTURE_3D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
    this.gl.texParameteri(this.gl.TEXTURE_3D, this.gl.TEXTURE_WRAP_R, this.gl.CLAMP_TO_EDGE);

    // 分配纹理存储
    if (data) {
      this.gl.texImage3D(
        this.gl.TEXTURE_3D, 0, this.gl.RGBA,
        width, height, depth, 0,
        this.gl.RGBA, this.gl.FLOAT, data
      );
    } else {
      this.gl.texImage3D(
        this.gl.TEXTURE_3D, 0, this.gl.RGBA,
        width, height, depth, 0,
        this.gl.RGBA, this.gl.FLOAT, null
      );
    }

    this.gl.bindTexture(this.gl.TEXTURE_3D, null);
    
    const textureInfo = {
      texture: texture,
      width: width,
      height: height,
      depth: depth
    };
    
    this.textures.set(texture, textureInfo);
    return texture;
  }

  // 更新3D纹理数据
  update3DTexture(texture, data) {
    const info = this.textures.get(texture);
    if (!info) return;

    this.gl.bindTexture(this.gl.TEXTURE_3D, texture);
    this.gl.texSubImage3D(
      this.gl.TEXTURE_3D, 0, 0, 0, 0,
      info.width, info.height, info.depth,
      this.gl.RGBA, this.gl.FLOAT, data
    );
    this.gl.bindTexture(this.gl.TEXTURE_3D, null);
  }

  // 创建体积数据
  createVolumeData(width, height, depth, generator) {
    const size = width * height * depth * 4; // RGBA
    const data = new Float32Array(size);
    
    for (let z = 0; z < depth; z++) {
      for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
          const index = (z * width * height + y * width + x) * 4;
          const value = generator(x, y, z, width, height, depth);
          
          data[index] = value.r || value;
          data[index + 1] = value.g || value;
          data[index + 2] = value.b || value;
          data[index + 3] = value.a || 1.0;
        }
      }
    }
    
    return data;
  }

  // 生成噪声体积
  generateNoiseVolume(width, height, depth, frequency = 0.1) {
    return this.createVolumeData(width, height, depth, (x, y, z, w, h, d) => {
      // 简单的3D噪声生成
      const nx = x / w * frequency;
      const ny = y / h * frequency;
      const nz = z / d * frequency;
      
      const noise = this.simplex3D(nx, ny, nz);
      return (noise + 1) * 0.5; // 归一化到 [0, 1]
    });
  }

  // 简化的3D噪声函数
  simplex3D(x, y, z) {
    // 简化实现 - 实际应该使用完整的噪声算法
    return Math.sin(x * 12.9898 + y * 78.233 + z * 144.7272) * 43758.5453 % 1;
  }
}

export default {
  name: 'WebGL2Features',
  setup() {
    const webgl2Canvas = ref(null);
    const webgl2Supported = ref(false);
    const rendererType = ref('未知');
    const gpuInfo = ref('未知');
    const particleCount = ref(10000);
    const simulationSpeed = ref(1.0);
    const physicsPrecision = ref(1.0);
    const computeShaderEnabled = ref(false);
    const transformFeedbackEnabled = ref(false);
    const realtimePhysicsEnabled = ref(false);
    const mrtBufferCount = ref(4);
    const gBufferResolution = ref(100);
    const mrtEnabled = ref(false);
    const deferredRenderingEnabled = ref(false);
    const hdrRenderingEnabled = ref(true);
    const instanceCount = ref(1000);
    const instancingLOD = ref(2);
    const instancingEnabled = ref(false);
    const dynamicInstancingEnabled = ref(false);
    const frustumCullingEnabled = ref(true);
    const texture3DResolution = ref(64);
    const arrayTextureLayers = ref(4);
    const texture3DEnabled = ref(false);
    const arrayTexturesEnabled = ref(false);
    const textureCompressionEnabled = ref(true);
    const drawCalls = ref(0);
    const instanceDraws = ref(0);
    const computeTime = ref(0);
    const currentFPS = ref(0);
    const gpuMemory = ref(0);
    const bufferCount = ref(0);
    const isLoading = ref(true);
    const loadingMessage = ref('检测 WebGL 2.0 支持...');
    const showDebugOverlay = ref(false);

    const featurePresets = [
      { id: 'basic', name: '基础渲染', features: [] },
      { id: 'compute', name: '计算着色器', features: ['compute', 'particles'] },
      { id: 'mrt', name: '多重渲染', features: ['mrt', 'deferred'] },
      { id: 'instancing', name: '实例化', features: ['instancing', 'dynamic'] },
      { id: 'advanced', name: '高级特性', features: ['compute', 'mrt', 'instancing', 'texture3d'] }
    ];

    const debugViews = [
      { id: 'final', name: '最终渲染' },
      { id: 'albedo', name: '漫反射' },
      { id: 'normal', name: '法线' },
      { id: 'position', name: '位置' },
      { id: 'depth', name: '深度' },
      { id: 'particles', name: '粒子' }
    ];

    const loadingFeatures = reactive([
      { name: 'WebGL 2.0 上下文', status: 'loading', statusText: '检测中...' },
      { name: '计算着色器', status: 'pending', statusText: '等待' },
      { name: '多重渲染目标', status: 'pending', statusText: '等待' },
      { name: '实例化渲染', status: 'pending', statusText: '等待' },
      { name: '3D 纹理', status: 'pending', statusText: '等待' }
    ]);

    let currentPreset = ref(featurePresets[0]);
    let currentDebugView = ref(debugViews[0]);
    let activeFeatures = ref([]);

    let scene, camera, renderer, controls;
    let computeSystem, mrtSystem, instancingSystem, texture3DSystem;
    let clock, stats;
    let frameCount = 0;
    let lastFpsUpdate = 0;

    // 初始化场景
    const initScene = async () => {
      // 检测 WebGL 2.0 支持
      loadingMessage.value = '检测 WebGL 2.0 支持...';
      await detectWebGL2Support();

      if (!webgl2Supported.value) {
        loadingFeatures[0].status = 'error';
        loadingFeatures[0].statusText = '不支持';
        isLoading.value = false;
        return;
      }

      loadingFeatures[0].status = 'success';
      loadingFeatures[0].statusText = '已支持';

      // 创建场景
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0x1a1a1a);

      // 创建相机
      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.position.set(5, 5, 5);

      // 创建 WebGL 2.0 渲染器
      loadingMessage.value = '创建 WebGL 2.0 渲染器...';
      await createWebGL2Renderer();

      // 初始化各系统
      loadingMessage.value = '初始化计算着色器系统...';
      await initializeComputeSystem();

      loadingMessage.value = '初始化多重渲染目标系统...';
      await initializeMRTSystem();

      loadingMessage.value = '初始化实例化渲染系统...';
      await initializeInstancingSystem();

      loadingMessage.value = '初始化 3D 纹理系统...';
      await initializeTexture3DSystem();

      // 创建测试场景
      loadingMessage.value = '创建测试场景...';
      await createTestScene();

      // 设置事件监听
      setupEventListeners();

      isLoading.value = false;
      
      // 启动渲染循环
      clock = new THREE.Clock();
      animate();
    };

    // 检测 WebGL 2.0 支持
    const detectWebGL2Support = async () => {
      const canvas = document.createElement('canvas');
      const gl = canvas.getContext('webgl2');
      
      webgl2Supported.value = gl !== null && gl instanceof WebGL2RenderingContext;
      
      if (webgl2Supported.value) {
        rendererType.value = 'WebGL 2.0';
        const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
        if (debugInfo) {
          gpuInfo.value = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
        }
      } else {
        rendererType.value = 'WebGL 1.0';
        gpuInfo.value = '不支持 WebGL 2.0';
      }
    };

    // 创建 WebGL 2.0 渲染器
    const createWebGL2Renderer = async () => {
      const canvas = webgl2Canvas.value;
      const context = canvas.getContext('webgl2');
      
      renderer = new THREE.WebGLRenderer({
        canvas: canvas,
        context: context,
        antialias: true,
        powerPreference: "high-performance"
      });
      
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.toneMappingExposure = 1.0;

      // 添加控制器
      const OrbitControls = (await import('three/addons/controls/OrbitControls.js')).OrbitControls;
      controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;

      loadingFeatures[1].status = 'success';
      loadingFeatures[1].statusText = '已初始化';
    };

    // 初始化计算着色器系统
    const initializeComputeSystem = async () => {
      computeSystem = new WebGL2ComputeSystem(renderer);
      
      if (computeSystem.isSupported) {
        loadingFeatures[1].status = 'success';
        loadingFeatures[1].statusText = '已支持';
      } else {
        loadingFeatures[1].status = 'warning';
        loadingFeatures[1].statusText = '部分支持';
      }
    };

    // 初始化多重渲染目标系统
    const initializeMRTSystem = async () => {
      mrtSystem = new MRTSystem(
        renderer, 
        window.innerWidth, 
        window.innerHeight, 
        mrtBufferCount.value
      );
      
      loadingFeatures[2].status = 'success';
      loadingFeatures[2].statusText = '已初始化';
    };

    // 初始化实例化渲染系统
    const initializeInstancingSystem = async () => {
      instancingSystem = new InstancingSystem();
      
      loadingFeatures[3].status = 'success';
      loadingFeatures[3].statusText = '已初始化';
    };

    // 初始化 3D 纹理系统
    const initializeTexture3DSystem = async () => {
      texture3DSystem = new Texture3DSystem(renderer);
      
      loadingFeatures[4].status = 'success';
      loadingFeatures[4].statusText = '已初始化';
    };

    // 创建测试场景
    const createTestScene = async () => {
      // 添加光照
      const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
      scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
      directionalLight.position.set(50, 50, 25);
      directionalLight.castShadow = true;
      scene.add(directionalLight);

      // 创建地面
      const groundGeometry = new THREE.PlaneGeometry(20, 20);
      const groundMaterial = new THREE.MeshStandardMaterial({ 
        color: 0x3a3a3a,
        roughness: 0.8,
        metalness: 0.2
      });
      const ground = new THREE.Mesh(groundGeometry, groundMaterial);
      ground.rotation.x = -Math.PI / 2;
      ground.receiveShadow = true;
      scene.add(ground);

      // 根据当前预设创建内容
      updateSceneContent();
    };

    // 更新场景内容
    const updateSceneContent = () => {
      // 清除现有内容(除了灯光和地面)
      const toRemove = [];
      scene.traverse((child) => {
        if (child.isMesh && child !== scene.children.find(c => c.geometry && c.geometry.type === 'PlaneGeometry')) {
          toRemove.push(child);
        }
      });
      toRemove.forEach(child => scene.remove(child));

      // 根据预设添加内容
      const preset = currentPreset.value;
      activeFeatures.value = preset.features;

      if (preset.features.includes('particles')) {
        createParticleSystem();
      }

      if (preset.features.includes('instancing')) {
        createInstancedObjects();
      }

      if (preset.features.includes('texture3d')) {
        create3DTextureDemo();
      }
    };

    // 创建粒子系统
    const createParticleSystem = () => {
      // 使用实例化网格创建粒子系统
      const particleGeometry = new THREE.SphereGeometry(0.1, 8, 6);
      const particleMaterial = new THREE.MeshStandardMaterial({
        color: 0x00aaff,
        roughness: 0.3,
        metalness: 0.1
      });

      const particleMesh = instancingSystem.createInstancedMesh(
        particleGeometry, 
        particleMaterial, 
        particleCount.value
      );

      // 初始化粒子位置
      for (let i = 0; i < particleCount.value; i++) {
        const position = new THREE.Vector3(
          (Math.random() - 0.5) * 10,
          Math.random() * 5,
          (Math.random() - 0.5) * 10
        );

        const rotation = new THREE.Quaternion();
        rotation.setFromEuler(new THREE.Euler(
          Math.random() * Math.PI,
          Math.random() * Math.PI,
          Math.random() * Math.PI
        ));

        const scale = new THREE.Vector3(1, 1, 1);
        const color = new THREE.Color(
          Math.random() * 0.5 + 0.5,
          Math.random() * 0.5 + 0.5,
          Math.random() * 0.5 + 0.5
        );

        instancingSystem.updateInstanceData(particleMesh.uuid, i, {
          position: position,
          rotation: rotation,
          scale: scale,
          color: color
        });
      }

      instancingSystem.applyInstanceData(particleMesh.uuid);
      scene.add(particleMesh);
    };

    // 创建实例化对象
    const createInstancedObjects = () => {
      const geometries = [
        new THREE.BoxGeometry(0.5, 0.5, 0.5),
        new THREE.SphereGeometry(0.3, 12, 8),
        new THREE.ConeGeometry(0.3, 0.8, 8)
      ];

      const materials = [
        new THREE.MeshStandardMaterial({ color: 0xff4444, roughness: 0.3 }),
        new THREE.MeshStandardMaterial({ color: 0x44ff44, roughness: 0.3 }),
        new THREE.MeshStandardMaterial({ color: 0x4444ff, roughness: 0.3 })
      ];

      for (let i = 0; i < 3; i++) {
        const instanceCount = Math.floor(instanceCount.value / 3);
        const instancedMesh = instancingSystem.createInstancedMesh(
          geometries[i], 
          materials[i], 
          instanceCount
        );

        for (let j = 0; j < instanceCount; j++) {
          const position = new THREE.Vector3(
            (Math.random() - 0.5) * 15,
            Math.random() * 3,
            (Math.random() - 0.5) * 15
          );

          const rotation = new THREE.Quaternion();
          rotation.setFromEuler(new THREE.Euler(
            Math.random() * Math.PI,
            Math.random() * Math.PI, 
            Math.random() * Math.PI
          ));

          const scale = new THREE.Vector3(
            Math.random() * 0.5 + 0.5,
            Math.random() * 0.5 + 0.5,
            Math.random() * 0.5 + 0.5
          );

          instancingSystem.updateInstanceData(instancedMesh.uuid, j, {
            position: position,
            rotation: rotation,
            scale: scale
          });
        }

        instancingSystem.applyInstanceData(instancedMesh.uuid);
        scene.add(instancedMesh);
      }
    };

    // 创建3D纹理演示
    const create3DTextureDemo = () => {
      // 创建体积数据
      const volumeData = texture3DSystem.generateNoiseVolume(
        texture3DResolution.value,
        texture3DResolution.value,
        texture3DResolution.value,
        0.05
      );

      // 创建3D纹理
      const texture3D = texture3DSystem.create3DTexture(
        texture3DResolution.value,
        texture3DResolution.value,
        texture3DResolution.value,
        volumeData
      );

      // 创建体积渲染平面
      const volumeGeometry = new THREE.PlaneGeometry(8, 8);
      const volumeMaterial = new THREE.ShaderMaterial({
        uniforms: {
          volumeTexture: { value: texture3D },
          volumeSize: { value: new THREE.Vector3(
            texture3DResolution.value,
            texture3DResolution.value,
            texture3DResolution.value
          )},
          time: { value: 0 }
        },
        vertexShader: `
          varying vec2 vUv;
          void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
          }
        `,
        fragmentShader: `
          uniform sampler3D volumeTexture;
          uniform vec3 volumeSize;
          uniform float time;
          varying vec2 vUv;
          
          void main() {
            vec3 coord = vec3(vUv, sin(time * 0.001) * 0.5 + 0.5);
            vec4 color = texture(volumeTexture, coord);
            gl_FragColor = vec4(color.rgb, 1.0);
          }
        `,
        transparent: true
      });

      const volumeMesh = new THREE.Mesh(volumeGeometry, volumeMaterial);
      volumeMesh.position.set(0, 3, 0);
      scene.add(volumeMesh);
    };

    // 设置事件监听
    const setupEventListeners = () => {
      window.addEventListener('resize', onWindowResize);
    };

    // 窗口大小变化
    const onWindowResize = () => {
      if (!camera || !renderer) return;
      
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
      
      if (mrtSystem) {
        mrtSystem.resize(window.innerWidth, window.innerHeight);
      }
    };

    // 加载特性预设
    const loadFeaturePreset = (preset) => {
      currentPreset.value = preset;
      updateSceneContent();
    };

    // 设置调试视图
    const setDebugView = (view) => {
      currentDebugView.value = view;
      // 实际实现应该切换渲染通道或着色器
    };

    // 启用 WebGL 1.0 回退
    const enableWebGL1Fallback = () => {
      console.log('启用 WebGL 1.0 回退模式');
      // 实际实现应该重新初始化使用 WebGL 1.0
    };

    // 显示兼容性信息
    const showCompatibilityInfo = () => {
      alert('WebGL 2.0 需要支持该标准的现代浏览器,如 Chrome 56+、Firefox 51+、Safari 15.2+、Edge 79+');
    };

    // 动画循环
    const animate = () => {
      requestAnimationFrame(animate);
      
      const deltaTime = clock.getDelta();
      
      // 更新控制器
      controls.update();
      
      // 更新计算着色器
      if (computeShaderEnabled.value && computeSystem && computeSystem.isSupported) {
        updateComputeShaders(deltaTime);
      }
      
      // 更新实例化对象
      if (instancingEnabled.value && dynamicInstancingEnabled.value) {
        updateInstancedObjects(deltaTime);
      }
      
      // 渲染场景
      renderScene();
      
      // 更新性能统计
      updatePerformanceStats(deltaTime);
    };

    // 更新计算着色器
    const updateComputeShaders = (deltaTime) => {
      const startTime = performance.now();
      
      // 这里应该执行实际的计算着色器代码
      // 简化实现 - 模拟计算时间
      if (computeSystem && computeSystem.isSupported) {
        // 模拟计算着色器执行
        setTimeout(() => {
          computeTime.value = performance.now() - startTime;
        }, 1);
      }
    };

    // 更新实例化对象
    const updateInstancedObjects = (deltaTime) => {
      // 动态更新实例位置和旋转
      instancingSystem.instancedMeshes.forEach((mesh, uuid) => {
        const data = instancingSystem.instanceData.get(uuid);
        if (!data) return;
        
        for (let i = 0; i < mesh.count; i++) {
          const idx = i * 3;
          const ridx = i * 4;
          
          // 简单动画:上下浮动和旋转
          data.positions[idx + 1] += Math.sin(Date.now() * 0.001 + i) * 0.01;
          
          // 更新旋转
          const rotation = new THREE.Quaternion();
          rotation.setFromEuler(new THREE.Euler(
            Date.now() * 0.0001,
            Date.now() * 0.0002 + i * 0.1,
            Date.now() * 0.0003
          ));
          
          data.rotations[ridx] = rotation.x;
          data.rotations[ridx + 1] = rotation.y;
          data.rotations[ridx + 2] = rotation.z;
          data.rotations[ridx + 3] = rotation.w;
        }
        
        instancingSystem.applyInstanceData(uuid);
      });
    };

    // 渲染场景
    const renderScene = () => {
      if (mrtEnabled.value && mrtSystem) {
        // 使用多重渲染目标
        mrtSystem.bind();
        renderer.render(scene, camera);
        mrtSystem.unbind();
        
        // 这里应该添加延迟着色或后处理
      } else {
        // 前向渲染
        renderer.render(scene, camera);
      }
    };

    // 更新性能统计
    const updatePerformanceStats = (deltaTime) => {
      frameCount++;
      lastFpsUpdate += deltaTime;
      
      if (lastFpsUpdate >= 1.0) {
        currentFPS.value = Math.round(frameCount / lastFpsUpdate);
        
        // 模拟性能数据
        drawCalls.value = Math.floor(Math.random() * 50) + 10;
        instanceDraws.value = instancingEnabled.value ? instanceCount.value : 0;
        gpuMemory.value = Math.floor(Math.random() * 500) * 1024 * 1024;
        bufferCount.value = Math.floor(Math.random() * 20) + 5;
        
        frameCount = 0;
        lastFpsUpdate = 0;
      }
    };

    // 计算属性
    const loadingProgressStyle = computed(() => ({
      width: '100%'
    }));

    // 工具函数
    const formatNumber = (num) => {
      if (num >= 1000000) {
        return (num / 1000000).toFixed(1) + 'M';
      } else if (num >= 1000) {
        return (num / 1000).toFixed(1) + 'K';
      }
      return num.toString();
    };

    const formatMemory = (bytes) => {
      if (bytes >= 1024 * 1024) {
        return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
      } else if (bytes >= 1024) {
        return (bytes / 1024).toFixed(1) + ' KB';
      }
      return bytes + ' B';
    };

    onMounted(() => {
      initScene();
    });

    onUnmounted(() => {
      if (renderer) {
        renderer.dispose();
      }
      if (mrtSystem) {
        mrtSystem.dispose();
      }
      window.removeEventListener('resize', onWindowResize);
    });

    return {
      webgl2Canvas,
      webgl2Supported,
      rendererType,
      gpuInfo,
      particleCount,
      simulationSpeed,
      physicsPrecision,
      computeShaderEnabled,
      transformFeedbackEnabled,
      realtimePhysicsEnabled,
      mrtBufferCount,
      gBufferResolution,
      mrtEnabled,
      deferredRenderingEnabled,
      hdrRenderingEnabled,
      instanceCount,
      instancingLOD,
      instancingEnabled,
      dynamicInstancingEnabled,
      frustumCullingEnabled,
      texture3DResolution,
      arrayTextureLayers,
      texture3DEnabled,
      arrayTexturesEnabled,
      textureCompressionEnabled,
      drawCalls,
      instanceDraws,
      computeTime,
      currentFPS,
      gpuMemory,
      bufferCount,
      isLoading,
      loadingMessage,
      showDebugOverlay,
      featurePresets,
      debugViews,
      loadingFeatures,
      currentPreset,
      currentDebugView,
      activeFeatures,
      loadingProgressStyle,
      loadFeaturePreset,
      setDebugView,
      enableWebGL1Fallback,
      showCompatibilityInfo,
      formatNumber,
      formatMemory
    };
  }
};
</script>

<style scoped>
.webgl2-features-container {
  width: 100%;
  height: 100vh;
  position: relative;
  background: #000;
  overflow: hidden;
}

.webgl2-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.features-controls {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 380px;
  background: rgba(0, 0, 0, 0.95);
  padding: 20px;
  border-radius: 12px;
  color: white;
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  max-height: 80vh;
  overflow-y: auto;
}

.control-section {
  margin-bottom: 25px;
  padding-bottom: 15px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.control-section:last-child {
  margin-bottom: 0;
  border-bottom: none;
}

.control-section h3 {
  color: #00aaff;
  margin-bottom: 15px;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.webgl2-status {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 15px;
}

.status-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  font-size: 14px;
}

.status-item span:first-child {
  color: #ccc;
}

.status-supported {
  color: #00ff88;
  font-weight: bold;
}

.status-unsupported {
  color: #ff4444;
  font-weight: bold;
}

.feature-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.preset-button {
  padding: 10px;
  border: 2px solid #444;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.05);
  color: white;
  cursor: pointer;
  font-size: 12px;
  transition: all 0.3s ease;
}

.preset-button:hover {
  border-color: #00aaff;
}

.preset-button.active {
  border-color: #00aaff;
  background: rgba(0, 170, 255, 0.2);
  box-shadow: 0 0 15px rgba(0, 170, 255, 0.3);
}

.control-group {
  margin-bottom: 15px;
}

.control-group label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  color: #ccc;
  font-size: 14px;
}

.control-group input[type="range"] {
  width: 100%;
  height: 6px;
  background: #444;
  border-radius: 3px;
  outline: none;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.control-group input[type="range"]:hover {
  opacity: 1;
}

.control-group input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #00aaff;
  cursor: pointer;
  box-shadow: 0 0 10px rgba(0, 170, 255, 0.5);
}

.control-group input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: #00aaff;
}

.compute-shader-controls,
.mrt-controls,
.instancing-controls,
.texture-controls {
  margin-bottom: 15px;
}

.compute-options,
.mrt-options,
.instancing-options,
.texture-options {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}

.performance-stats {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  font-size: 12px;
}

.stat-item span:first-child {
  color: #ccc;
}

.stat-item span:last-child {
  color: #00ff88;
  font-weight: bold;
}

.debug-views {
  position: absolute;
  bottom: 20px;
  left: 20px;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.debug-button {
  padding: 8px 12px;
  border: 1px solid #444;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  cursor: pointer;
  font-size: 11px;
  transition: all 0.3s ease;
}

.debug-button:hover {
  border-color: #00aaff;
}

.debug-button.active {
  border-color: #00aaff;
  background: rgba(0, 170, 255, 0.2);
}

.features-info {
  position: absolute;
  top: 20px;
  left: 20px;
  background: rgba(0, 0, 0, 0.8);
  padding: 15px;
  border-radius: 8px;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.info-panel h4 {
  color: #00aaff;
  margin-bottom: 10px;
  font-size: 14px;
}

.active-features {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.feature-tag {
  padding: 4px 8px;
  background: rgba(0, 170, 255, 0.2);
  border: 1px solid #00aaff;
  border-radius: 12px;
  font-size: 10px;
  color: #00aaff;
}

.webgl2-indicator {
  position: absolute;
  top: 20px;
  right: 420px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: bold;
  backdrop-filter: blur(10px);
}

.webgl2-indicator.supported {
  background: rgba(0, 255, 136, 0.2);
  border: 1px solid #00ff88;
  color: #00ff88;
}

.webgl2-indicator.unsupported {
  background: rgba(255, 68, 68, 0.2);
  border: 1px solid #ff4444;
  color: #ff4444;
}

.indicator-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.webgl2-indicator.supported .indicator-dot {
  background: #00ff88;
  box-shadow: 0 0 10px #00ff88;
}

.webgl2-indicator.unsupported .indicator-dot {
  background: #ff4444;
  box-shadow: 0 0 10px #ff4444;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.loading-content {
  text-align: center;
  color: white;
}

.webgl2-loader {
  margin-bottom: 30px;
}

.gpu-chip {
  width: 120px;
  height: 120px;
  background: linear-gradient(45deg, #00aaff, #0088cc);
  border-radius: 20px;
  margin: 0 auto;
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 10px;
  padding: 15px;
  animation: chipGlow 2s infinite alternate;
}

.chip-core {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 8px;
  animation: corePulse 1.5s infinite ease-in-out;
}

.chip-core:nth-child(1) { animation-delay: 0s; }
.chip-core:nth-child(2) { animation-delay: 0.3s; }
.chip-core:nth-child(3) { animation-delay: 0.6s; }
.chip-core:nth-child(4) { animation-delay: 0.9s; }

.loading-content h3 {
  margin-bottom: 20px;
  color: white;
  font-size: 20px;
}

.loading-progress {
  width: 400px;
  margin: 0 auto 20px;
}

.progress-bar {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 10px;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #00aaff, #00ff88);
  border-radius: 3px;
  transition: width 0.3s ease;
}

.feature-loading {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 400px;
  margin: 0 auto;
}

.feature-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 6px;
  font-size: 14px;
}

.feature-item span:last-child.loading {
  color: #ffaa00;
}

.feature-item span:last-child.success {
  color: #00ff88;
}

.feature-item span:last-child.error {
  color: #ff4444;
}

.feature-item span:last-child.warning {
  color: #ffaa00;
}

.feature-item span:last-child.pending {
  color: #888;
}

.unsupported-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.9);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.unsupported-content {
  text-align: center;
  color: white;
  background: rgba(255, 68, 68, 0.1);
  padding: 40px;
  border-radius: 12px;
  border: 1px solid rgba(255, 68, 68, 0.3);
  backdrop-filter: blur(20px);
  max-width: 500px;
}

.warning-icon {
  font-size: 48px;
  margin-bottom: 20px;
}

.unsupported-content h3 {
  margin-bottom: 15px;
  color: #ff4444;
}

.unsupported-content p {
  margin-bottom: 25px;
  color: #ccc;
  line-height: 1.5;
}

.fallback-options {
  display: flex;
  gap: 15px;
  justify-content: center;
}

.fallback-button,
.info-button {
  padding: 12px 20px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.3s ease;
}

.fallback-button {
  background: rgba(0, 170, 255, 0.8);
  color: white;
}

.fallback-button:hover {
  background: rgba(0, 170, 255, 1);
  transform: translateY(-2px);
}

.info-button {
  background: rgba(255, 255, 255, 0.1);
  color: white;
  border: 1px solid rgba(255, 255, 255, 0.3);
}

.info-button:hover {
  background: rgba(255, 255, 255, 0.2);
}

@keyframes chipGlow {
  0% {
    box-shadow: 0 0 20px rgba(0, 170, 255, 0.5);
  }
  100% {
    box-shadow: 0 0 40px rgba(0, 170, 255, 0.8);
  }
}

@keyframes corePulse {
  0%, 100% {
    opacity: 0.7;
    transform: scale(1);
  }
  50% {
    opacity: 1;
    transform: scale(1.1);
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .features-controls {
    width: 320px;
    right: 10px;
    top: 10px;
    padding: 15px;
  }
  
  .feature-presets {
    grid-template-columns: 1fr;
  }
  
  .webgl2-indicator {
    right: 340px;
  }
  
  .features-info {
    left: 10px;
    top: 10px;
  }
  
  .debug-views {
    left: 10px;
    bottom: 10px;
  }
  
  .loading-progress,
  .feature-loading {
    width: 300px;
  }
}
</style>

高级WebGL 2.0特性实现

统一缓冲区对象(UBO)系统

javascript 复制代码
// 统一缓冲区管理系统
class UniformBufferSystem {
  constructor(renderer) {
    this.renderer = renderer;
    this.gl = renderer.getContext();
    this.buffers = new Map();
    this.bindings = new Map();
    this.nextBindingPoint = 0;
  }

  // 创建统一缓冲区
  createUniformBuffer(name, size, usage = this.gl.DYNAMIC_DRAW) {
    const buffer = this.gl.createBuffer();
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, buffer);
    this.gl.bufferData(this.gl.UNIFORM_BUFFER, size, usage);
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null);
    
    const bufferInfo = {
      buffer: buffer,
      size: size,
      data: new ArrayBuffer(size),
      view: new DataView(new ArrayBuffer(size))
    };
    
    this.buffers.set(name, bufferInfo);
    return bufferInfo;
  }

  // 绑定统一缓冲区到着色器
  bindUniformBuffer(name, program, blockName, bindingPoint = this.nextBindingPoint++) {
    const bufferInfo = this.buffers.get(name);
    if (!bufferInfo) return;
    
    const blockIndex = this.gl.getUniformBlockIndex(program, blockName);
    if (blockIndex === this.gl.INVALID_INDEX) {
      console.warn(`Uniform block ${blockName} not found in program`);
      return;
    }
    
    this.gl.uniformBlockBinding(program, blockIndex, bindingPoint);
    this.gl.bindBufferBase(this.gl.UNIFORM_BUFFER, bindingPoint, bufferInfo.buffer);
    
    this.bindings.set(name, { program, blockName, bindingPoint });
  }

  // 更新统一缓冲区数据
  updateUniformBuffer(name, data, offset = 0) {
    const bufferInfo = this.buffers.get(name);
    if (!bufferInfo) return;
    
    // 将数据复制到ArrayBuffer
    if (data instanceof Float32Array) {
      new Float32Array(bufferInfo.data).set(data, offset / 4);
    } else if (data instanceof Int32Array) {
      new Int32Array(bufferInfo.data).set(data, offset / 4);
    } else if (data instanceof Uint32Array) {
      new Uint32Array(bufferInfo.data).set(data, offset / 4);
    } else {
      console.warn('Unsupported data type for uniform buffer');
      return;
    }
    
    // 上传到GPU
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, bufferInfo.buffer);
    this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, offset, data);
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null);
  }

  // 设置统一缓冲区的标量值
  setUniformBufferScalar(name, offset, value) {
    const bufferInfo = this.buffers.get(name);
    if (!bufferInfo) return;
    
    bufferInfo.view.setFloat32(offset, value, true);
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, bufferInfo.buffer);
    this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, offset, new Float32Array([value]));
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null);
  }

  // 设置统一缓冲区的向量值
  setUniformBufferVector(name, offset, x, y, z, w = 0) {
    const bufferInfo = this.buffers.get(name);
    if (!bufferInfo) return;
    
    const data = new Float32Array([x, y, z, w]);
    bufferInfo.view.setFloat32(offset, x, true);
    bufferInfo.view.setFloat32(offset + 4, y, true);
    bufferInfo.view.setFloat32(offset + 8, z, true);
    bufferInfo.view.setFloat32(offset + 12, w, true);
    
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, bufferInfo.buffer);
    this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, offset, data);
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null);
  }

  // 设置统一缓冲区的矩阵值
  setUniformBufferMatrix(name, offset, matrix) {
    const bufferInfo = this.buffers.get(name);
    if (!bufferInfo) return;
    
    const data = new Float32Array(16);
    for (let i = 0; i < 16; i++) {
      data[i] = matrix.elements[i];
      bufferInfo.view.setFloat32(offset + i * 4, matrix.elements[i], true);
    }
    
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, bufferInfo.buffer);
    this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, offset, data);
    this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null);
  }

  // 获取绑定点
  getBindingPoint(name) {
    const binding = this.bindings.get(name);
    return binding ? binding.bindingPoint : -1;
  }

  // 清理资源
  dispose() {
    for (const bufferInfo of this.buffers.values()) {
      this.gl.deleteBuffer(bufferInfo.buffer);
    }
    this.buffers.clear();
    this.bindings.clear();
  }
}

// 顶点数组对象(VAO)管理系统
class VertexArraySystem {
  constructor(renderer) {
    this.renderer = renderer;
    this.gl = renderer.getContext();
    this.vaos = new Map();
  }

  // 创建顶点数组对象
  createVertexArray(name) {
    const vao = this.gl.createVertexArray();
    this.vaos.set(name, vao);
    return vao;
  }

  // 绑定顶点数组对象
  bindVertexArray(name) {
    const vao = this.vaos.get(name);
    if (vao) {
      this.gl.bindVertexArray(vao);
    } else {
      this.gl.bindVertexArray(null);
    }
  }

  // 设置顶点属性指针
  setupVertexAttributes(vaoName, attributes) {
    this.bindVertexArray(vaoName);
    
    attributes.forEach(attr => {
      const { buffer, size, type, normalized, stride, offset, divisor } = attr;
      
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
      this.gl.enableVertexAttribArray(attr.location);
      this.gl.vertexAttribPointer(
        attr.location, size, type, normalized, stride, offset
      );
      
      // 设置实例化除数
      if (divisor !== undefined) {
        this.gl.vertexAttribDivisor(attr.location, divisor);
      }
    });
    
    this.gl.bindVertexArray(null);
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
  }

  // 绘制顶点数组对象
  drawVertexArray(vaoName, mode, count, instances = 1) {
    const vao = this.vaos.get(vaoName);
    if (!vao) return;
    
    this.gl.bindVertexArray(vao);
    
    if (instances > 1) {
      this.gl.drawArraysInstanced(mode, 0, count, instances);
    } else {
      this.gl.drawArrays(mode, 0, count);
    }
    
    this.gl.bindVertexArray(null);
  }

  // 删除顶点数组对象
  deleteVertexArray(name) {
    const vao = this.vaos.get(name);
    if (vao) {
      this.gl.deleteVertexArray(vao);
      this.vaos.delete(name);
    }
  }

  // 清理所有顶点数组对象
  dispose() {
    for (const vao of this.vaos.values()) {
      this.gl.deleteVertexArray(vao);
    }
    this.vaos.clear();
  }
}

注意事项与最佳实践

  1. 性能优化策略

    • 优先使用统一缓冲区对象减少API调用
    • 利用顶点数组对象优化状态切换
    • 使用实例化渲染减少绘制调用
    • 合理使用变换反馈减少CPU-GPU数据传输
  2. 兼容性处理

    • 提供WebGL 1.0回退方案
    • 检测特性支持并优雅降级
    • 使用扩展检测确保功能可用性
    • 为不同硬件提供多级质量预设
  3. 内存管理

    • 及时删除不再使用的缓冲区和纹理
    • 使用对象池重用WebGL资源
    • 监控GPU内存使用情况
    • 实现资源的延迟加载和卸载
  4. 调试与开发

    • 使用WebGL调试扩展
    • 实现详细的错误检查和处理
    • 提供性能分析工具
    • 支持着色器热重载

下一节预告

第39节:3D打印输出与模型导出准备

将深入探索3D模型导出技术,包括:几何体拓扑检查、STL/OBJ格式导出、3D打印优化、网格修复,以及如何为不同制造工艺准备3D模型。

相关推荐
xiaoxue..2 小时前
深入理解 JavaScript 异步编程:从单线程到 Promise 的完整指南
前端·javascript·面试·node.js
MediaTea2 小时前
Python 第三方库:Markdown(将文本渲染为 HTML)
开发语言·前端·python·html
倚肆3 小时前
HTMLElement 与MouseEvent 事件对象属性详解
前端·javascript
Halo_tjn3 小时前
Java 基于分支和循环结构的专项实验
java·开发语言·计算机
洛_尘3 小时前
数据结构--9:反射、枚举以及lambda表达式(了解即可)
java·开发语言·数据结构
青衫码上行3 小时前
【Java Web学习 | 第12篇】JavaScript(6)DOM
java·开发语言·前端·javascript·学习
杜子不疼.3 小时前
【C++】 set/multiset底层原理与逻辑详解
java·开发语言·c++
Macbethad3 小时前
如何用WPF做工控设置界面
java·开发语言·wpf
大炮走火3 小时前
iOS在制作framework时,oc与swift混编的流程及坑点!
开发语言·ios·swift