第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 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:
// 检测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高级特性演示
<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;}}// 应用实例数据到GPUapplyInstanceData(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; // RGBAconst 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)系统
// 统一缓冲区管理系统
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;// 将数据复制到ArrayBufferif (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;}// 上传到GPUthis.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();}
}
注意事项与最佳实践
-
性能优化策略
- 优先使用统一缓冲区对象减少API调用
- 利用顶点数组对象优化状态切换
- 使用实例化渲染减少绘制调用
- 合理使用变换反馈减少CPU-GPU数据传输
-
兼容性处理
- 提供WebGL 1.0回退方案
- 检测特性支持并优雅降级
- 使用扩展检测确保功能可用性
- 为不同硬件提供多级质量预设
-
内存管理
- 及时删除不再使用的缓冲区和纹理
- 使用对象池重用WebGL资源
- 监控GPU内存使用情况
- 实现资源的延迟加载和卸载
-
调试与开发
- 使用WebGL调试扩展
- 实现详细的错误检查和处理
- 提供性能分析工具
- 支持着色器热重载
下一节预告
第39节:3D打印输出与模型导出准备
将深入探索3D模型导出技术,包括:几何体拓扑检查、STL/OBJ格式导出、3D打印优化、网格修复,以及如何为不同制造工艺准备3D模型。
