You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Three.js渲染的Canvas背景设置为透明?

问题:Three.js Canvas背景无法设置为透明

我已经尝试在JS中设置setClearColor,也在CSS中设置Canvas的透明属性,但Canvas背景仍为黑色,希望将其改为透明。

渲染页面截图


HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <link rel="stylesheet" href="/src/index.css" />
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@v0.150.1/build/three.module.js",
          "three/addons/": "https://unpkg.com/three@v0.150.1/examples/jsm/"
        }
      }
    </script>
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>EXAMPLE</title>
  </head>
  <body>
    <div id="root"></div>
    <div class="globe-render" id="globe-render">
      <script type="module" src="./globe.js"></script>
    </div>
  </body>
</html>

CSS代码

@tailwind base;
@tailwind components;
@tailwind utilities;
body {
  background-color: #000f14;
  margin: 0;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}

#globe-render {
  position: absolute;
  top: -20%;
  left: 20%;
  background-color: rgb(255, 255, 255);
}
#myCanvas {
  position: absolute;
  background-color: green;
}

JS代码

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";

//Radius define
let ParticleSurfaceLayer = 7.5;
let GlobeRadius = 6.6;

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const container = document.querySelector(".globe-render");
renderer.setClearColor(0x000000,0);
container.appendChild(renderer.domElement);


// Group for globe and particle system
const group = new THREE.Group();
scene.add(group);

// Bloom pass for the globe
const renderScene = new RenderPass(scene, camera);
const globeBloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,
  0.4,
  0.85
);
globeBloomPass.threshold = 0;
globeBloomPass.strength = 2;
globeBloomPass.radius = 0;

const globeComposer = new EffectComposer(renderer);
globeComposer.setSize(window.innerWidth, window.innerHeight);
globeComposer.addPass(renderScene);
globeComposer.addPass(globeBloomPass);

// Bloom pass for particles
const particleComposer = new EffectComposer(renderer);
particleComposer.setSize(window.innerWidth, window.innerHeight);
particleComposer.addPass(new RenderPass(group, camera)); // Assuming 'group' contains particles
const particleBloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,
  0.4,
  0.85
);

particleBloomPass.threshold = 0;
particleBloomPass.strength = 1.3;
particleBloomPass.radius = 0.8;
particleComposer.addPass(particleBloomPass);

// Update size function
function updateSize() {
  const newWidth = window.innerWidth;
  const newHeight = window.innerHeight;

  camera.aspect = newWidth / newHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(newWidth, newHeight);

  const sphereRadius = Math.min(newWidth, newHeight) * 0.1;
  globe.geometry = new THREE.SphereGeometry(sphereRadius, 32, 32);

  const particleSizeMin = Math.min(newWidth, newHeight) * 0.001;
  const particleSizeMax = Math.min(newWidth, newHeight) * 0.004;

  group.children.forEach((particle) => {
    const randomSize = THREE.MathUtils.randFloat(
      particleSizeMin,
      particleSizeMax
    );
    particle.scale.set(randomSize, randomSize, randomSize);
  });

  // Update composer sizes
  globeComposer.setSize(newWidth, newHeight);
  particleComposer.setSize(newWidth, newHeight);
}

//orbit controls
const controls = new OrbitControls(camera, renderer.domElement);
const loader = new THREE.TextureLoader();
controls.enableZoom = false;
// controls.enabled=false

// Initial setup
////////
const geometry = new THREE.SphereGeometry(GlobeRadius, 80, 80);
const material1 = new THREE.MeshBasicMaterial({
  map: loader.load("./8k_earth_nightmap_underlayer.jpg"),
  //transparent: true,
  opacity: 1,
});
const material2 = new THREE.MeshBasicMaterial({
  color: 0xff047e,
  transparent: true,
  opacity: 0.1,
});
const multimaterial = [material1, material2];
const globe = new THREE.Mesh(geometry, material1);
globe.layers.set(1);
group.add(globe);

// Particle System
const particleCount = 600;
const color = new THREE.Color("#fc2414");

for (let i = 0; i < particleCount; i++) {
  // ... (same as your code)

  const phi = Math.random() * Math.PI * 2;
  const theta = Math.random() * Math.PI - Math.PI / 2;
  const randomradius = 0.01 + Math.random() * 0.04;

  const radius = ParticleSurfaceLayer; // Radius of the sphere

  const x = radius * Math.cos(theta) * Math.cos(phi);
  const y = radius * Math.cos(theta) * Math.sin(phi);
  const z = radius * Math.sin(theta);

  const particleGeometry = new THREE.SphereGeometry(randomradius, 30, 25); // Initial size
  const particleMaterial = new THREE.MeshBasicMaterial({
    color: "#00FFFF",
  });
  const particle = new THREE.Mesh(particleGeometry, particleMaterial);

  particle.position.set(x, y, z);
  particle.layers.set(1);
  group.add(particle);
}

const ambientLight = new THREE.AmbientLight(0xffffff, 100);
scene.add(ambientLight);

// Camera position
camera.position.z = 15;

// Rotation animation
const rotationSpeed = 0.001;

// Animation function
const animate = function () {
  requestAnimationFrame(animate);

  group.rotation.y += rotationSpeed;

  // Render globe with bloom effect
  camera.layers.set(1);
  globeComposer.render();

  // Render particles with bloom effect
  particleComposer.render();
};
///////orbit controls///*
let isDragging = false;
let originalRotation = group.rotation.y;

// Event listener for mouse down
renderer.domElement.addEventListener("mousedown", () => {
  isDragging = true;
});

// Event listener for mouse up
renderer.domElement.addEventListener("mouseup", () => {
  isDragging = false;
  // Reset the rotation to its original position
  group.rotation.y = originalRotation;
});

// Event listener for mouse leave (in case mouse leaves the canvas while dragging)
renderer.domElement.addEventListener("mouseleave", () => {
  if (isDragging) {
    isDragging = false;
    // Reset the rotation to its original position
    group.rotation.y = originalRotation;
  }
});

// Handle window resize
window.addEventListener("resize", updateSize);

// Start animation
animate();

const canvas = document.querySelector("canvas");
canvas.id = "myCanvas";
canvas.classList.add("myCanvasClass");

解决方案

问题出在你使用了两个EffectComposer进行渲染,默认情况下EffectComposer会创建不透明的渲染目标,覆盖了你设置的透明背景。需要给每个EffectComposer配置支持透明的渲染目标,并调整渲染逻辑。

修改步骤:

  1. 为每个Composer配置透明渲染目标
    创建EffectComposer时,传入自定义的WebGLRenderTarget,开启alpha通道:

    // 替换 globeComposer 创建代码
    const globeRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
      format: THREE.RGBAFormat,
      alpha: true
    });
    const globeComposer = new EffectComposer(renderer, globeRenderTarget);
    
    // 替换 particleComposer 创建代码
    const particleRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
      format: THREE.RGBAFormat,
      alpha: true
    });
    const particleComposer = new EffectComposer(renderer, particleRenderTarget);
    
  2. 调整渲染时的缓冲区清除逻辑
    修改animate函数,确保渲染时保留透明通道,避免第二个Composer覆盖背景:

    const animate = function () {
      requestAnimationFrame(animate);
    
      group.rotation.y += rotationSpeed;
    
      // 渲染前清除颜色和深度缓冲区,保留透明
      renderer.setClearColor(0x000000, 0);
      renderer.clear();
      
      camera.layers.set(1);
      globeComposer.render();
    
      // 渲染粒子时只清除深度缓冲区,保留已有透明内容
      renderer.clearDepth();
      particleComposer.render();
    };
    
  3. 移除CSS中遮挡透明的背景色
    删除或注释掉以下CSS规则:

    #globe-render {
      /* background-color: rgb(255, 255, 255); */
    }
    #myCanvas {
      /* background-color: green; */
    }
    

完成以上修改后,Canvas背景会变为透明,你可以看到页面的#000f14背景色。

内容的提问来源于stack exchange,提问作者B ANURAG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:34:55