[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-a8d9f571-9c1e-4545-a7b5-ad23f77ca34a":3,"$ftXVj8zYr9_unjnMGPStouZHSbbE2GX4z-b1_zy546Fo":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"a8d9f571-9c1e-4545-a7b5-ad23f77ca34a","threejs-postprocessing","Three.js后处理 - EffectComposer、光晕、景深、屏幕效果。用于添加视觉效果、色彩分级、模糊、发光或创建自定义屏幕空间着色器。","cat_life_career","mod_other","sickn33,other","---\nname: threejs-postprocessing\ndescription: Three.js post-processing - EffectComposer, bloom, DOF, screen effects. Use when adding visual effects, color grading, blur, glow, or creating custom screen-space shaders.\nrisk: unknown\nsource: community\n---\n\n# Three.js Post-Processing\n\n## When to Use\n- You need screen-space visual effects in a Three.js render pipeline.\n- The task involves `EffectComposer`, bloom, depth of field, color grading, blur, or custom passes.\n- You are enhancing the final rendered image rather than base scene setup alone.\n\n## Quick Start\n\n```javascript\nimport * as THREE from \"three\";\nimport { EffectComposer } from \"three\u002Faddons\u002Fpostprocessing\u002FEffectComposer.js\";\nimport { RenderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FRenderPass.js\";\nimport { UnrealBloomPass } from \"three\u002Faddons\u002Fpostprocessing\u002FUnrealBloomPass.js\";\n\n\u002F\u002F Setup composer\nconst composer = new EffectComposer(renderer);\n\n\u002F\u002F Render scene\nconst renderPass = new RenderPass(scene, camera);\ncomposer.addPass(renderPass);\n\n\u002F\u002F Add bloom\nconst bloomPass = new UnrealBloomPass(\n  new THREE.Vector2(window.innerWidth, window.innerHeight),\n  1.5, \u002F\u002F strength\n  0.4, \u002F\u002F radius\n  0.85, \u002F\u002F threshold\n);\ncomposer.addPass(bloomPass);\n\n\u002F\u002F Animation loop - use composer instead of renderer\nfunction animate() {\n  requestAnimationFrame(animate);\n  composer.render(); \u002F\u002F NOT renderer.render()\n}\n```\n\n## EffectComposer Setup\n\n```javascript\nimport { EffectComposer } from \"three\u002Faddons\u002Fpostprocessing\u002FEffectComposer.js\";\nimport { RenderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FRenderPass.js\";\n\nconst composer = new EffectComposer(renderer);\n\n\u002F\u002F First pass: render scene\nconst renderPass = new RenderPass(scene, camera);\ncomposer.addPass(renderPass);\n\n\u002F\u002F Add more passes...\ncomposer.addPass(effectPass);\n\n\u002F\u002F Last pass should render to screen\neffectPass.renderToScreen = true; \u002F\u002F Default for last pass\n\n\u002F\u002F Handle resize\nfunction onResize() {\n  const width = window.innerWidth;\n  const height = window.innerHeight;\n\n  camera.aspect = width \u002F height;\n  camera.updateProjectionMatrix();\n\n  renderer.setSize(width, height);\n  composer.setSize(width, height);\n}\n```\n\n## Common Effects\n\n### Bloom (Glow)\n\n```javascript\nimport { UnrealBloomPass } from \"three\u002Faddons\u002Fpostprocessing\u002FUnrealBloomPass.js\";\n\nconst bloomPass = new UnrealBloomPass(\n  new THREE.Vector2(window.innerWidth, window.innerHeight),\n  1.5, \u002F\u002F strength - intensity of glow\n  0.4, \u002F\u002F radius - spread of glow\n  0.85, \u002F\u002F threshold - brightness threshold\n);\n\ncomposer.addPass(bloomPass);\n\n\u002F\u002F Adjust at runtime\nbloomPass.strength = 2.0;\nbloomPass.threshold = 0.5;\nbloomPass.radius = 0.8;\n```\n\n### Selective Bloom\n\nApply bloom only to specific objects.\n\n```javascript\nimport { UnrealBloomPass } from \"three\u002Faddons\u002Fpostprocessing\u002FUnrealBloomPass.js\";\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\n\n\u002F\u002F Layer setup\nconst BLOOM_LAYER = 1;\nconst bloomLayer = new THREE.Layers();\nbloomLayer.set(BLOOM_LAYER);\n\n\u002F\u002F Mark objects to bloom\nglowingMesh.layers.enable(BLOOM_LAYER);\n\n\u002F\u002F Dark material for non-blooming objects\nconst darkMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });\nconst materials = {};\n\nfunction darkenNonBloomed(obj) {\n  if (obj.isMesh && !bloomLayer.test(obj.layers)) {\n    materials[obj.uuid] = obj.material;\n    obj.material = darkMaterial;\n  }\n}\n\nfunction restoreMaterial(obj) {\n  if (materials[obj.uuid]) {\n    obj.material = materials[obj.uuid];\n    delete materials[obj.uuid];\n  }\n}\n\n\u002F\u002F Custom render loop\nfunction render() {\n  \u002F\u002F Render bloom pass\n  scene.traverse(darkenNonBloomed);\n  composer.render();\n  scene.traverse(restoreMaterial);\n\n  \u002F\u002F Render final scene over bloom\n  renderer.render(scene, camera);\n}\n```\n\n### FXAA (Anti-Aliasing)\n\n```javascript\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\nimport { FXAAShader } from \"three\u002Faddons\u002Fshaders\u002FFXAAShader.js\";\n\nconst fxaaPass = new ShaderPass(FXAAShader);\nfxaaPass.material.uniforms[\"resolution\"].value.set(\n  1 \u002F window.innerWidth,\n  1 \u002F window.innerHeight,\n);\n\ncomposer.addPass(fxaaPass);\n\n\u002F\u002F Update on resize\nfunction onResize() {\n  fxaaPass.material.uniforms[\"resolution\"].value.set(\n    1 \u002F window.innerWidth,\n    1 \u002F window.innerHeight,\n  );\n}\n```\n\n### SMAA (Better Anti-Aliasing)\n\n```javascript\nimport { SMAAPass } from \"three\u002Faddons\u002Fpostprocessing\u002FSMAAPass.js\";\n\nconst smaaPass = new SMAAPass(\n  window.innerWidth * renderer.getPixelRatio(),\n  window.innerHeight * renderer.getPixelRatio(),\n);\n\ncomposer.addPass(smaaPass);\n```\n\n### SSAO (Ambient Occlusion)\n\n```javascript\nimport { SSAOPass } from \"three\u002Faddons\u002Fpostprocessing\u002FSSAOPass.js\";\n\nconst ssaoPass = new SSAOPass(\n  scene,\n  camera,\n  window.innerWidth,\n  window.innerHeight,\n);\nssaoPass.kernelRadius = 16;\nssaoPass.minDistance = 0.005;\nssaoPass.maxDistance = 0.1;\n\ncomposer.addPass(ssaoPass);\n\n\u002F\u002F Output modes\nssaoPass.output = SSAOPass.OUTPUT.Default;\n\u002F\u002F SSAOPass.OUTPUT.Default - Final composited output\n\u002F\u002F SSAOPass.OUTPUT.SSAO - Just the AO\n\u002F\u002F SSAOPass.OUTPUT.Blur - Blurred AO\n\u002F\u002F SSAOPass.OUTPUT.Depth - Depth buffer\n\u002F\u002F SSAOPass.OUTPUT.Normal - Normal buffer\n```\n\n### Depth of Field (DOF)\n\n```javascript\nimport { BokehPass } from \"three\u002Faddons\u002Fpostprocessing\u002FBokehPass.js\";\n\nconst bokehPass = new BokehPass(scene, camera, {\n  focus: 10.0, \u002F\u002F Focus distance\n  aperture: 0.025, \u002F\u002F Aperture (smaller = more DOF)\n  maxblur: 0.01, \u002F\u002F Max blur amount\n});\n\ncomposer.addPass(bokehPass);\n\n\u002F\u002F Update focus dynamically\nbokehPass.uniforms[\"focus\"].value = distanceToTarget;\n```\n\n### Film Grain\n\n```javascript\nimport { FilmPass } from \"three\u002Faddons\u002Fpostprocessing\u002FFilmPass.js\";\n\nconst filmPass = new FilmPass(\n  0.35, \u002F\u002F noise intensity\n  0.5, \u002F\u002F scanline intensity\n  648, \u002F\u002F scanline count\n  false, \u002F\u002F grayscale\n);\n\ncomposer.addPass(filmPass);\n```\n\n### Vignette\n\n```javascript\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\nimport { VignetteShader } from \"three\u002Faddons\u002Fshaders\u002FVignetteShader.js\";\n\nconst vignettePass = new ShaderPass(VignetteShader);\nvignettePass.uniforms[\"offset\"].value = 1.0; \u002F\u002F Vignette size\nvignettePass.uniforms[\"darkness\"].value = 1.0; \u002F\u002F Vignette intensity\n\ncomposer.addPass(vignettePass);\n```\n\n### Color Correction\n\n```javascript\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\nimport { ColorCorrectionShader } from \"three\u002Faddons\u002Fshaders\u002FColorCorrectionShader.js\";\n\nconst colorPass = new ShaderPass(ColorCorrectionShader);\ncolorPass.uniforms[\"powRGB\"].value = new THREE.Vector3(1.2, 1.2, 1.2); \u002F\u002F Power\ncolorPass.uniforms[\"mulRGB\"].value = new THREE.Vector3(1.0, 1.0, 1.0); \u002F\u002F Multiply\n\ncomposer.addPass(colorPass);\n```\n\n### Gamma Correction\n\n```javascript\nimport { GammaCorrectionShader } from \"three\u002Faddons\u002Fshaders\u002FGammaCorrectionShader.js\";\n\nconst gammaPass = new ShaderPass(GammaCorrectionShader);\ncomposer.addPass(gammaPass);\n```\n\n### Pixelation\n\n```javascript\nimport { RenderPixelatedPass } from \"three\u002Faddons\u002Fpostprocessing\u002FRenderPixelatedPass.js\";\n\nconst pixelPass = new RenderPixelatedPass(6, scene, camera); \u002F\u002F 6 = pixel size\n\ncomposer.addPass(pixelPass);\n```\n\n### Glitch Effect\n\n```javascript\nimport { GlitchPass } from \"three\u002Faddons\u002Fpostprocessing\u002FGlitchPass.js\";\n\nconst glitchPass = new GlitchPass();\nglitchPass.goWild = false; \u002F\u002F Continuous glitching\n\ncomposer.addPass(glitchPass);\n```\n\n### Halftone\n\n```javascript\nimport { HalftonePass } from \"three\u002Faddons\u002Fpostprocessing\u002FHalftonePass.js\";\n\nconst halftonePass = new HalftonePass(window.innerWidth, window.innerHeight, {\n  shape: 1, \u002F\u002F 1 = dot, 2 = ellipse, 3 = line, 4 = square\n  radius: 4, \u002F\u002F Dot size\n  rotateR: Math.PI \u002F 12,\n  rotateB: (Math.PI \u002F 12) * 2,\n  rotateG: (Math.PI \u002F 12) * 3,\n  scatter: 0,\n  blending: 1,\n  blendingMode: 1,\n  greyscale: false,\n});\n\ncomposer.addPass(halftonePass);\n```\n\n### Outline\n\n```javascript\nimport { OutlinePass } from \"three\u002Faddons\u002Fpostprocessing\u002FOutlinePass.js\";\n\nconst outlinePass = new OutlinePass(\n  new THREE.Vector2(window.innerWidth, window.innerHeight),\n  scene,\n  camera,\n);\n\noutlinePass.edgeStrength = 3;\noutlinePass.edgeGlow = 0;\noutlinePass.edgeThickness = 1;\noutlinePass.pulsePeriod = 0;\noutlinePass.visibleEdgeColor.set(0xffffff);\noutlinePass.hiddenEdgeColor.set(0x190a05);\n\n\u002F\u002F Select objects to outline\noutlinePass.selectedObjects = [mesh1, mesh2];\n\ncomposer.addPass(outlinePass);\n```\n\n## Custom ShaderPass\n\nCreate your own post-processing effects.\n\n```javascript\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\n\nconst CustomShader = {\n  uniforms: {\n    tDiffuse: { value: null }, \u002F\u002F Required: input texture\n    time: { value: 0 },\n    intensity: { value: 1.0 },\n  },\n  vertexShader: `\n    varying vec2 vUv;\n\n    void main() {\n      vUv = uv;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    }\n  `,\n  fragmentShader: `\n    uniform sampler2D tDiffuse;\n    uniform float time;\n    uniform float intensity;\n    varying vec2 vUv;\n\n    void main() {\n      vec2 uv = vUv;\n\n      \u002F\u002F Wave distortion\n      uv.x += sin(uv.y * 10.0 + time) * 0.01 * intensity;\n\n      vec4 color = texture2D(tDiffuse, uv);\n      gl_FragColor = color;\n    }\n  `,\n};\n\nconst customPass = new ShaderPass(CustomShader);\ncomposer.addPass(customPass);\n\n\u002F\u002F Update in animation loop\ncustomPass.uniforms.time.value = clock.getElapsedTime();\n```\n\n### Invert Colors Shader\n\n```javascript\nconst InvertShader = {\n  uniforms: {\n    tDiffuse: { value: null },\n  },\n  vertexShader: `\n    varying vec2 vUv;\n    void main() {\n      vUv = uv;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    }\n  `,\n  fragmentShader: `\n    uniform sampler2D tDiffuse;\n    varying vec2 vUv;\n\n    void main() {\n      vec4 color = texture2D(tDiffuse, vUv);\n      gl_FragColor = vec4(1.0 - color.rgb, color.a);\n    }\n  `,\n};\n```\n\n### Chromatic Aberration\n\n```javascript\nconst ChromaticAberrationShader = {\n  uniforms: {\n    tDiffuse: { value: null },\n    amount: { value: 0.005 },\n  },\n  vertexShader: `\n    varying vec2 vUv;\n    void main() {\n      vUv = uv;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    }\n  `,\n  fragmentShader: `\n    uniform sampler2D tDiffuse;\n    uniform float amount;\n    varying vec2 vUv;\n\n    void main() {\n      vec2 dir = vUv - 0.5;\n      float dist = length(dir);\n\n      float r = texture2D(tDiffuse, vUv - dir * amount * dist).r;\n      float g = texture2D(tDiffuse, vUv).g;\n      float b = texture2D(tDiffuse, vUv + dir * amount * dist).b;\n\n      gl_FragColor = vec4(r, g, b, 1.0);\n    }\n  `,\n};\n```\n\n## Combining Multiple Effects\n\n```javascript\nimport { EffectComposer } from \"three\u002Faddons\u002Fpostprocessing\u002FEffectComposer.js\";\nimport { RenderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FRenderPass.js\";\nimport { UnrealBloomPass } from \"three\u002Faddons\u002Fpostprocessing\u002FUnrealBloomPass.js\";\nimport { ShaderPass } from \"three\u002Faddons\u002Fpostprocessing\u002FShaderPass.js\";\nimport { FXAAShader } from \"three\u002Faddons\u002Fshaders\u002FFXAAShader.js\";\nimport { VignetteShader } from \"three\u002Faddons\u002Fshaders\u002FVignetteShader.js\";\nimport { GammaCorrectionShader } from \"three\u002Faddons\u002Fshaders\u002FGammaCorrectionShader.js\";\n\nconst composer = new EffectComposer(renderer);\n\n\u002F\u002F 1. Render scene\ncomposer.addPass(new RenderPass(scene, camera));\n\n\u002F\u002F 2. Bloom\nconst bloomPass = new UnrealBloomPass(\n  new THREE.Vector2(window.innerWidth, window.innerHeight),\n  0.5,\n  0.4,\n  0.85,\n);\ncomposer.addPass(bloomPass);\n\n\u002F\u002F 3. Vignette\nconst vignettePass = new ShaderPass(VignetteShader);\nvignettePass.uniforms[\"offset\"].value = 0.95;\nvignettePass.uniforms[\"darkness\"].value = 1.0;\ncomposer.addPass(vignettePass);\n\n\u002F\u002F 4. Gamma correction\ncomposer.addPass(new ShaderPass(GammaCorrectionShader));\n\n\u002F\u002F 5. Anti-aliasing (always last before output)\nconst fxaaPass = new ShaderPass(FXAAShader);\nfxaaPass.uniforms[\"resolution\"].value.set(\n  1 \u002F window.innerWidth,\n  1 \u002F window.innerHeight,\n);\ncomposer.addPass(fxaaPass);\n```\n\n## Render to Texture\n\n```javascript\n\u002F\u002F Create render target\nconst renderTarget = new THREE.WebGLRenderTarget(512, 512);\n\n\u002F\u002F Render scene to target\nrenderer.setRenderTarget(renderTarget);\nrenderer.render(scene, camera);\nrenderer.setRenderTarget(null);\n\n\u002F\u002F Use texture\nconst texture = renderTarget.texture;\notherMaterial.map = texture;\n```\n\n## Multi-Pass Rendering\n\n```javascript\n\u002F\u002F Multiple composers for different scenes\u002Flayers\nconst bgComposer = new EffectComposer(renderer);\nbgComposer.addPass(new RenderPass(bgScene, camera));\n\nconst fgComposer = new EffectComposer(renderer);\nfgComposer.addPass(new RenderPass(fgScene, camera));\nfgComposer.addPass(bloomPass);\n\n\u002F\u002F Combine in render loop\nfunction animate() {\n  \u002F\u002F Render background without clearing\n  renderer.autoClear = false;\n  renderer.clear();\n\n  bgComposer.render();\n\n  \u002F\u002F Render foreground over it\n  renderer.clearDepth();\n  fgComposer.render();\n}\n```\n\n## WebGPU Post-Processing (Three.js r183)\n\nThe WebGPU renderer uses a node-based `PostProcessing` class instead of `EffectComposer`. Note that `EffectComposer` is **WebGL-only**.\n\n```javascript\nimport * as THREE from \"three\";\nimport { pass, bloom, dof } from \"three\u002Ftsl\";\nimport { WebGPURenderer } from \"three\u002Faddons\u002Frenderers\u002Fwebgpu\u002FWebGPURenderer.js\";\n\nconst renderer = new WebGPURenderer({ antialias: true });\nawait renderer.init();\n\n\u002F\u002F Create post-processing\nconst postProcessing = new THREE.PostProcessing(renderer);\n\n\u002F\u002F Scene pass\nconst scenePass = pass(scene, camera);\n\n\u002F\u002F Add bloom\nconst bloomPass = bloom(scenePass, 0.5, 0.4, 0.85);\n\n\u002F\u002F Set output\npostProcessing.outputNode = bloomPass;\n\n\u002F\u002F Render\nrenderer.setAnimationLoop(() => {\n  postProcessing.render();\n});\n```\n\n### Key Differences from EffectComposer\n\n| EffectComposer (WebGL)          | PostProcessing (WebGPU)          |\n| ------------------------------- | -------------------------------- |\n| `addPass(new RenderPass(...))`  | `pass(scene, camera)`            |\n| `addPass(new UnrealBloomPass)` | `bloom(scenePass, ...)`          |\n| `composer.render()`             | `postProcessing.render()`        |\n| Chain of passes                 | Node graph with `outputNode`     |\n| GLSL shader passes              | TSL node-based effects           |\n\n## Performance Tips\n\n1. **Limit passes**: Each pass adds a full-screen render\n2. **Lower resolution**: Use smaller render targets for blur passes\n3. **Disable unused effects**: Toggle passes on\u002Foff\n4. **Use FXAA over MSAA**: Less expensive anti-aliasing\n5. **Profile with DevTools**: Check GPU usage\n\n```javascript\n\u002F\u002F Disable pass\nbloomPass.enabled = false;\n\n\u002F\u002F Reduce bloom resolution\nconst bloomPass = new UnrealBloomPass(\n  new THREE.Vector2(window.innerWidth \u002F 2, window.innerHeight \u002F 2),\n  strength,\n  radius,\n  threshold,\n);\n\n\u002F\u002F Only apply effects in high-performance scenarios\nconst isMobile = \u002FiPhone|iPad|Android\u002Fi.test(navigator.userAgent);\nif (!isMobile) {\n  composer.addPass(expensivePass);\n}\n```\n\n## Handle Resize\n\n```javascript\nfunction onWindowResize() {\n  const width = window.innerWidth;\n  const height = window.innerHeight;\n  const pixelRatio = renderer.getPixelRatio();\n\n  camera.aspect = width \u002F height;\n  camera.updateProjectionMatrix();\n\n  renderer.setSize(width, height);\n  composer.setSize(width, height);\n\n  \u002F\u002F Update pass-specific resolutions\n  if (fxaaPass) {\n    fxaaPass.material.uniforms[\"resolution\"].value.set(\n      1 \u002F (width * pixelRatio),\n      1 \u002F (height * pixelRatio),\n    );\n  }\n\n  if (bloomPass) {\n    bloomPass.resolution.set(width, height);\n  }\n}\n\nwindow.addEventListener(\"resize\", onWindowResize);\n```\n\n## See Also\n\n- `threejs-shaders` - Custom shader development\n- `threejs-textures` - Render targets\n- `threejs-fundamentals` - Renderer setup\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,113,1488,"2026-05-16 13:44:17",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"其他","other","mdi-page-next-outline","其他类型Skill",5,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"职场发展","career","mdi-briefcase-outline","面试准备、简历优化、职业规划",4,575,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"dcc9e214-c680-4a03-9044-60f9a733c0bc","1.0.0","threejs-postprocessing.zip",4445,"uploads\u002Fskills\u002Fa8d9f571-9c1e-4545-a7b5-ad23f77ca34a\u002Fthreejs-postprocessing.zip","187259f82a891b1d7b8604eeab3c5e93952aecff61b910345dfe3a78cd1f6497","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":15892}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]