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