javascript
// 避免频繁创建/销毁RenderTarget
const rt = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
});
// 复用RenderTarget
function updateRenderTargetSize(rt, width, height) {
if (rt.width !== width || rt.height !== height) {
rt.setSize(width, height);
}
}
javascript
// 降低后处理分辨率(质量vs性能权衡)
const pixelRatio = renderer.getPixelRatio();
const downSampleRatio = 0.5; // 50%分辨率
const postProcessingTarget = new THREE.WebGLRenderTarget(
Math.floor(width * downSampleRatio * pixelRatio),
Math.floor(height * downSampleRatio * pixelRatio)
);
javascript
class OptimizedEffectComposer extends THREE.EffectComposer {
constructor(renderer, renderTarget) {
super(renderer, renderTarget);
this.enabledPasses = new Set();
}
addPass(pass) {
super.addPass(pass);
this.enabledPasses.add(pass);
pass.enabled = true;
}
setPassEnabled(pass, enabled) {
if (this.enabledPasses.has(pass)) {
pass.enabled = enabled;
}
}
}
javascript
// 合并多个效果到单个ShaderPass
const customShader = {
uniforms: {
tDiffuse: { value: null },
uBloomStrength: { value: 0.5 },
uVignetteStrength: { value: 0.8 },
uChromaticAberration: { value: 0.005 }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uBloomStrength;
uniform float uVignetteStrength;
uniform float uChromaticAberration;
varying vec2 vUv;
void main() {
// 合并多个效果
vec2 uv = vUv;
vec2 center = vec2(0.5);
// 色差效果
float r = texture2D(tDiffuse, uv + (center - uv) * uChromaticAberration).r;
float g = texture2D(tDiffuse, uv).g;
float b = texture2D(tDiffuse, uv - (center - uv) * uChromaticAberration).b;
vec3 color = vec3(r, g, b);
// 渐晕效果
float vignette = 1.0 - distance(uv, center) * uVignetteStrength;
color *= vignette;
gl_FragColor = vec4(color, 1.0);
}
`
};
javascript
class AdaptivePostProcessing {
constructor(composer) {
this.composer = composer;
this.qualityMode = 'high'; // high, medium, low
this.frameTimeHistory = [];
this.targetFPS = 60;
}
updateQuality() {
const avgFrameTime = this.getAverageFrameTime();
const currentFPS = 1000 / avgFrameTime;
if (currentFPS < this.targetFPS * 0.8) {
this.lowerQuality();
} else if (currentFPS > this.targetFPS * 1.2) {
this.raiseQuality();
}
}
lowerQuality() {
switch(this.qualityMode) {
case 'high':
this.setBloomStrength(0.5);
this.setAAEnabled(false);
this.qualityMode = 'medium';
break;
case 'medium':
this.setDownSampleRatio(0.75);
this.qualityMode = 'low';
break;
}
}
raiseQuality() {
switch(this.qualityMode) {
case 'low':
this.setDownSampleRatio(1.0);
this.qualityMode = 'medium';
break;
case 'medium':
this.setBloomStrength(1.0);
this.setAAEnabled(true);
this.qualityMode = 'high';
break;
}
}
}
javascript
// 只对需要后处理的物体进行渲染
const postProcessScene = new THREE.Scene();
const postProcessCamera = new THREE.Camera();
// 在主渲染时标记需要后处理的物体
function renderSelectively() {
// 第一次渲染:不需要后处理的物体
renderer.setRenderTarget(null);
scene.traverse(obj => {
if (obj.userData.skipPostProcessing) {
obj.visible = true;
} else {
obj.visible = false;
}
});
renderer.render(scene, camera);
// 第二次渲染:需要后处理的物体到RenderTarget
renderer.setRenderTarget(postProcessTarget);
scene.traverse(obj => {
if (!obj.userData.skipPostProcessing) {
obj.visible = true;
} else {
obj.visible = false;
}
});
renderer.render(scene, camera);
// 应用后处理
composer.setRenderTarget(postProcessTarget);
composer.render();
}
javascript
// 对于大量重复物体,使用Instancing减少draw calls
const geometry = new THREE.InstancedBufferGeometry();
// ... 设置geometry
const material = new THREE.ShaderMaterial({
uniforms: {
uPostProcessingData: { value: new THREE.Vector4() }
},
vertexShader: `
attribute vec3 instanceOffset;
attribute float instanceScale;
void main() {
vec3 pos = position * instanceScale + instanceOffset;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
uniform vec4 uPostProcessingData;
void main() {
// 为后处理输出额外数据
gl_FragColor = vec4(color, uPostProcessingData.x);
}
`
});
javascript
const texturePool = new Map();
function getTexture(key, width, height) {
if (!texturePool.has(key)) {
texturePool.set(key, new THREE.WebGLRenderTarget(width, height));
}
return texturePool.get(key);
}
javascript
class PingPongRender {
constructor(renderer, width, height) {
this.renderTargetA = new THREE.WebGLRenderTarget(width, height);
this.renderTargetB = new THREE.WebGLRenderTarget(width, height);
this.currentTarget = this.renderTargetA;
this.previousTarget = this.renderTargetB;
}
swap() {
[this.currentTarget, this.previousTarget] =
[this.previousTarget, this.currentTarget];
}
getCurrent() { return this.currentTarget; }
getPrevious() { return this.previousTarget; }
}
javascript
// 使用G-Buffer进行延迟渲染
const gBuffer = {
position: new THREE.WebGLRenderTarget(width, height, {
format: THREE.RGBAFormat,
type: THREE.FloatType
}),
normal: new THREE.WebGLRenderTarget(width, height, {
format: THREE.RGBAFormat
}),
albedo: new THREE.WebGLRenderTarget(width, height, {
format: THREE.RGBAFormat
})
};
// 延迟后处理Shader
const deferredPostShader = {
uniforms: {
tPosition: { value: null },
tNormal: { value: null },
tAlbedo: { value: null },
// ... 其他uniforms
},
// ... shader代码
};
javascript
class PostProcessingProfiler {
constructor(composer) {
this.composer = composer;
this.passTimings = new Map();
this.frameStart = 0;
}
startFrame() {
this.frameStart = performance.now();
}
measurePass(pass, callback) {
const start = performance.now();
callback();
const duration = performance.now() - start;
if (!this.passTimings.has(pass)) {
this.passTimings.set(pass, []);
}
const timings = this.passTimings.get(pass);
timings.push(duration);
// 保持最近100帧的数据
if (timings.length > 100) {
timings.shift();
}
return duration;
}
getPassAverage(pass) {
const timings = this.passTimings.get(pass);
if (!timings || timings.length === 0) return 0;
return timings.reduce((a, b) => a + b) / timings.length;
}
logPerformance() {
console.group('Post Processing Performance');
this.composer.passes.forEach(pass => {
const avg = this.getPassAverage(pass);
console.log(`${pass.constructor.name}: ${avg.toFixed(2)}ms`);
});
console.groupEnd();
}
}
开发阶段:使用全质量后处理
测试阶段:启用性能监控,识别瓶颈
优化阶段:
合并相似的ShaderPass
降低非关键效果的分辨率
实现动态质量调整
考虑使用WebGL 2.0的特性(如MRT)
发布阶段:
提供质量预设选项
根据设备能力自动调整
确保最低配置设备可流畅运行
javascript
const capabilities = {
isWebGL2: typeof WebGL2RenderingContext !== 'undefined',
maxTextureSize: renderer.capabilities.maxTextureSize,
maxPrecision: renderer.capabilities.precision,
floatTextures: renderer.extensions.has('OES_texture_float'),
drawBuffers: renderer.extensions.has('WEBGL_draw_buffers')
};
function getRecommendedQuality(capabilities) {
if (capabilities.isWebGL2 &&
capabilities.maxTextureSize >= 4096 &&
capabilities.floatTextures) {
return 'high';
} else if (capabilities.maxTextureSize >= 2048) {
return 'medium';
}
return 'low';
}
这些实践可以帮助你在保持视觉效果的同时,确保Three.js后处理的性能优化。关键是根据具体应用场景平衡视觉效果和性能消耗。