javascript
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
// 1. 定义着色器材质
const CustomShader = {
uniforms: {
"tDiffuse": { value: null }, // 主纹理
"uTime": { value: 0.0 }, // 时间
"uResolution": { value: new THREE.Vector2(1, 1) }, // 分辨率
"uStrength": { value: 1.0 }, // 效果强度
"uColor": { value: new THREE.Color(1.0, 1.0, 1.0) } // 颜色
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uTime;
uniform vec2 uResolution;
uniform float uStrength;
uniform vec3 uColor;
varying vec2 vUv;
void main() {
// 获取原始颜色
vec4 color = texture2D(tDiffuse, vUv);
// 应用效果
// 这里可以添加各种图像处理算法
gl_FragColor = color;
}
`
};
// 2. 创建着色器通道
const customPass = new ShaderPass(CustomShader);
// 3. 创建后处理合成器
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(customPass);
// 4. 在动画循环中更新
function animate() {
customPass.uniforms.uTime.value += 0.016; // 更新时间
composer.render();
requestAnimationFrame(animate);
}
javascript
const BlurShader = {
uniforms: {
"tDiffuse": { value: null },
"uStrength": { value: 2.0 },
"uDirection": { value: new THREE.Vector2(1.0, 0.0) }
},
vertexShader: `/* 同上 */`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uStrength;
uniform vec2 uDirection;
uniform vec2 uResolution;
varying vec2 vUv;
// 高斯核权重
float gaussian(float x, float sigma) {
return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * 3.14159) * sigma);
}
void main() {
vec2 texelSize = 1.0 / uResolution;
vec4 color = vec4(0.0);
float total = 0.0;
// 7x7 高斯模糊
for (float i = -3.0; i <= 3.0; i++) {
float weight = gaussian(i, uStrength);
vec2 offset = uDirection * i * texelSize;
color += texture2D(tDiffuse, vUv + offset) * weight;
total += weight;
}
gl_FragColor = color / total;
}
`
};
// 双通道模糊(水平和垂直)
const blurPass1 = new ShaderPass(BlurShader);
blurPass1.uniforms.uDirection.value.set(1.0, 0.0); // 水平模糊
const blurPass2 = new ShaderPass(BlurShader);
blurPass2.uniforms.uDirection.value.set(0.0, 1.0); // 垂直模糊
javascript
const EdgeDetectionShader = {
uniforms: {
"tDiffuse": { value: null },
"uThreshold": { value: 0.1 },
"uEdgeColor": { value: new THREE.Color(0.0, 0.0, 0.0) },
"uBackgroundColor": { value: new THREE.Color(1.0, 1.0, 1.0) }
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uThreshold;
uniform vec3 uEdgeColor;
uniform vec3 uBackgroundColor;
uniform vec2 uResolution;
varying vec2 vUv;
// Sobel 算子
float sobel(vec2 uv) {
vec2 texelSize = 1.0 / uResolution;
// 3x3 像素采样
float topLeft = texture2D(tDiffuse, uv + vec2(-1, 1) * texelSize).r;
float top = texture2D(tDiffuse, uv + vec2(0, 1) * texelSize).r;
float topRight = texture2D(tDiffuse, uv + vec2(1, 1) * texelSize).r;
float left = texture2D(tDiffuse, uv + vec2(-1, 0) * texelSize).r;
float right = texture2D(tDiffuse, uv + vec2(1, 0) * texelSize).r;
float bottomLeft = texture2D(tDiffuse, uv + vec2(-1, -1) * texelSize).r;
float bottom = texture2D(tDiffuse, uv + vec2(0, -1) * texelSize).r;
float bottomRight = texture2D(tDiffuse, uv + vec2(1, -1) * texelSize).r;
// Sobel 卷积核
float gx = -topLeft - 2.0 * left - bottomLeft + topRight + 2.0 * right + bottomRight;
float gy = -topLeft - 2.0 * top - topRight + bottomLeft + 2.0 * bottom + bottomRight;
return sqrt(gx * gx + gy * gy);
}
void main() {
float edge = sobel(vUv);
if (edge > uThreshold) {
gl_FragColor = vec4(uEdgeColor, 1.0);
} else {
vec4 original = texture2D(tDiffuse, vUv);
gl_FragColor = vec4(mix(original.rgb, uBackgroundColor, 0.2), original.a);
}
}
`
};
javascript
const PixelationShader = {
uniforms: {
"tDiffuse": { value: null },
"uPixelSize": { value: 8.0 },
"uResolution": { value: new THREE.Vector2(1, 1) }
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uPixelSize;
uniform vec2 uResolution;
varying vec2 vUv;
void main() {
// 计算像素化的UV
vec2 pixelSize = vec2(uPixelSize) / uResolution;
vec2 pixelatedUV = floor(vUv / pixelSize) * pixelSize + pixelSize * 0.5;
// 采样像素中心颜色
vec4 color = texture2D(tDiffuse, pixelatedUV);
// 添加网格效果
vec2 grid = mod(vUv / pixelSize, 1.0);
float gridLine = step(0.95, grid.x) + step(0.95, grid.y);
color.rgb = mix(color.rgb, vec3(0.0), gridLine * 0.3);
gl_FragColor = color;
}
`
};
javascript
const ColorGradingShader = {
uniforms: {
"tDiffuse": { value: null },
"uBrightness": { value: 0.0 },
"uContrast": { value: 1.0 },
"uSaturation": { value: 1.0 },
"uExposure": { value: 1.0 },
"uGamma": { value: 2.2 },
"uTemperature": { value: 0.0 }, // -1冷色到1暖色
"uTint": { value: 0.0 }, // -1绿到1紫
"uVignette": { value: 0.5 } // 暗角强度
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uBrightness;
uniform float uContrast;
uniform float uSaturation;
uniform float uExposure;
uniform float uGamma;
uniform float uTemperature;
uniform float uTint;
uniform float uVignette;
varying vec2 vUv;
// 计算亮度
float luminance(vec3 rgb) {
return dot(rgb, vec3(0.2126, 0.7152, 0.0722));
}
// 色温调整
vec3 adjustTemperature(vec3 color, float temp) {
vec3 warm = vec3(1.0, 0.7, 0.4);
vec3 cool = vec3(0.4, 0.7, 1.0);
if (temp > 0.0) {
return mix(color, color * warm, temp);
} else {
return mix(color, color * cool, -temp);
}
}
// 色调调整
vec3 adjustTint(vec3 color, float tint) {
vec3 green = vec3(0.4, 1.0, 0.4);
vec3 purple = vec3(1.0, 0.4, 1.0);
if (tint > 0.0) {
return mix(color, color * purple, tint);
} else {
return mix(color, color * green, -tint);
}
}
// 暗角效果
float vignette(vec2 uv) {
uv = uv * 2.0 - 1.0;
float dist = length(uv);
return smoothstep(0.8, 0.2, dist * (1.0 + uVignette));
}
void main() {
vec4 texel = texture2D(tDiffuse, vUv);
vec3 color = texel.rgb;
// 亮度调整
color += uBrightness;
// 对比度调整
color = ((color - 0.5) * max(uContrast, 0.0)) + 0.5;
// 饱和度调整
float lum = luminance(color);
color = mix(vec3(lum), color, uSaturation);
// 曝光调整
color *= uExposure;
// 色温调整
color = adjustTemperature(color, uTemperature);
// 色调调整
color = adjustTint(color, uTint);
// Gamma校正
color = pow(color, vec3(1.0 / uGamma));
// 应用暗角
color *= vignette(vUv);
gl_FragColor = vec4(color, texel.a);
}
`
};
javascript
const DepthOfFieldShader = {
uniforms: {
"tDiffuse": { value: null },
"tDepth": { value: null }, // 深度纹理
"focus": { value: 10.0 },
"aperture": { value: 0.025 },
"maxBlur": { value: 1.0 },
"near": { value: 0.1 },
"far": { value: 1000.0 }
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform sampler2D tDepth;
uniform float focus;
uniform float aperture;
uniform float maxBlur;
uniform float near;
uniform float far;
uniform vec2 uResolution;
varying vec2 vUv;
// 深度值转换
float getLinearDepth(vec2 uv) {
float depth = texture2D(tDepth, uv).r;
return (2.0 * near) / (far + near - depth * (far - near));
}
// 计算模糊半径
float getBlurRadius(float depth) {
float coc = abs(depth - focus) * aperture;
return clamp(coc, 0.0, maxBlur);
}
void main() {
float centerDepth = getLinearDepth(vUv);
float radius = getBlurRadius(centerDepth);
vec2 texelSize = 1.0 / uResolution;
vec4 color = vec4(0.0);
float totalWeight = 0.0;
// 圆形采样
int samples = 16;
float angleStep = 2.0 * 3.14159 / float(samples);
for (int i = 0; i < samples; i++) {
float angle = float(i) * angleStep;
vec2 offset = vec2(cos(angle), sin(angle)) * radius * texelSize;
for (float r = 0.5; r <= 2.5; r += 1.0) {
vec2 sampleUV = vUv + offset * r;
float weight = 1.0 / r;
color += texture2D(tDiffuse, sampleUV) * weight;
totalWeight += weight;
}
}
color /= totalWeight;
gl_FragColor = color;
}
`
};
javascript
const SSAOShader = {
uniforms: {
"tDiffuse": { value: null },
"tDepth": { value: null },
"tNormal": { value: null }, // 法线纹理
"uKernel": { value: [] },
"uNoise": { value: null },
"uRadius": { value: 0.5 },
"uBias": { value: 0.025 },
"uPower": { value: 2.0 },
"near": { value: 0.1 },
"far": { value: 1000.0 }
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform sampler2D tDepth;
uniform sampler2D tNormal;
uniform sampler2D uNoise;
uniform float uRadius;
uniform float uBias;
uniform float uPower;
uniform float near;
uniform float far;
uniform vec2 uResolution;
uniform vec3 uKernel[64];
varying vec2 vUv;
// 重建世界位置
vec3 getViewPosition(vec2 uv, float depth) {
vec2 ndc = uv * 2.0 - 1.0;
vec4 clipPos = vec4(ndc, depth * 2.0 - 1.0, 1.0);
vec4 viewPos = inverse(projectionMatrix) * clipPos;
return viewPos.xyz / viewPos.w;
}
void main() {
float depth = texture2D(tDepth, vUv).r;
vec3 viewPos = getViewPosition(vUv, depth);
vec3 normal = normalize(texture2D(tNormal, vUv).xyz * 2.0 - 1.0);
// 随机旋转
vec2 noiseScale = uResolution / 4.0;
vec3 randomVec = texture2D(uNoise, vUv * noiseScale).xyz;
// 创建切线空间矩阵
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
float occlusion = 0.0;
int kernelSize = 64;
for (int i = 0; i < kernelSize; i++) {
// 获取采样位置
vec3 samplePos = tbn * uKernel[i];
samplePos = viewPos + samplePos * uRadius;
// 投影到屏幕空间
vec4 offset = projectionMatrix * vec4(samplePos, 1.0);
offset.xyz /= offset.w;
offset.xyz = offset.xyz * 0.5 + 0.5;
// 获取采样深度
float sampleDepth = getViewPosition(offset.xy, texture2D(tDepth, offset.xy).r).z;
// 范围检查
float rangeCheck = smoothstep(0.0, 1.0, uRadius / abs(viewPos.z - sampleDepth));
// 累加遮蔽
occlusion += (sampleDepth >= samplePos.z + uBias ? 1.0 : 0.0) * rangeCheck;
}
occlusion = 1.0 - (occlusion / float(kernelSize));
occlusion = pow(occlusion, uPower);
vec4 color = texture2D(tDiffuse, vUv);
gl_FragColor = vec4(color.rgb * occlusion, color.a);
}
`
};
javascript
const MotionBlurShader = {
uniforms: {
"tDiffuse": { value: null },
"tDepth": { value: null },
"uVelocity": { value: null }, // 速度纹理
"uBlurSize": { value: 10.0 },
"uDeltaTime": { value: 0.016 }
},
fragmentShader: `
uniform sampler2D tDiffuse;
uniform sampler2D tDepth;
uniform sampler2D uVelocity;
uniform float uBlurSize;
uniform float uDeltaTime;
uniform vec2 uResolution;
varying vec2 vUv;
void main() {
// 获取速度
vec2 velocity = texture2D(uVelocity, vUv).rg;
velocity *= uBlurSize * uDeltaTime;
// 计算模糊
vec4 color = texture2D(tDiffuse, vUv);
if (length(velocity) > 0.0) {
int samples = 8;
for (int i = 1; i <= samples; i++) {
float weight = 1.0 / float(samples);
vec2 offset = velocity * (float(i) / float(samples));
color += texture2D(tDiffuse, vUv + offset) * weight;
color += texture2D(tDiffuse, vUv - offset) * weight;
}
color /= float(samples * 2 + 1);
}
gl_FragColor = color;
}
`
};
javascript
class AdaptiveShader {
constructor() {
this.shaders = {
high: HighQualityShader,
medium: MediumQualityShader,
low: LowQualityShader
};
this.currentQuality = 'high';
}
updateQuality(fps) {
if (fps < 30) {
this.currentQuality = 'low';
} else if (fps < 45) {
this.currentQuality = 'medium';
} else {
this.currentQuality = 'high';
}
}
getShader() {
return this.shaders[this.currentQuality];
}
}
javascript
class MultiPassEffect {
constructor(renderer) {
this.renderer = renderer;
// 创建多个渲染目标
this.renderTargets = [
new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
}),
new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
})
];
this.currentTarget = 0;
}
pingPong() {
this.currentTarget = 1 - this.currentTarget;
return this.renderTargets[this.currentTarget];
}
render(pass) {
const readTarget = this.renderTargets[this.currentTarget];
const writeTarget = this.pingPong();
pass.uniforms.tDiffuse.value = readTarget.texture;
this.renderer.setRenderTarget(writeTarget);
pass.render(this.renderer, writeTarget, 0);
return writeTarget.texture;
}
}
javascript
class ShaderDebugger {
static addDebugUI(shaderPass, gui) {
const folder = gui.addFolder('Shader调试');
// 添加所有uniforms到GUI
for (const [key, uniform] of Object.entries(shaderPass.material.uniforms)) {
if (uniform.value instanceof THREE.Vector2) {
folder.add(uniform.value, 'x', -10, 10).name(`${key}.x`);
folder.add(uniform.value, 'y', -10, 10).name(`${key}.y`);
} else if (uniform.value instanceof THREE.Vector3) {
folder.add(uniform.value, 'x', -10, 10).name(`${key}.x`);
folder.add(uniform.value, 'y', -10, 10).name(`${key}.y`);
folder.add(uniform.value, 'z', -10, 10).name(`${key}.z`);
} else if (uniform.value instanceof THREE.Color) {
folder.addColor(uniform.value, 'value').name(key);
} else if (typeof uniform.value === 'number') {
folder.add(uniform.value, 'value', -10, 10).name(key);
}
}
// 添加性能监视
const perf = {
enabled: true,
lastTime: performance.now(),
frames: 0,
fps: 60
};
folder.add(perf, 'enabled').name('启用效果');
folder.add(perf, 'fps').listen().name('FPS');
shaderPass.enabled = perf.enabled;
return { folder, perf };
}
}
这些自定义后处理着色器可以组合使用,创建复杂的视觉效果。记得根据具体需求优化性能,特别是采样次数和纹理大小。