Three.js Points与BufferGeometry:如何将点渲染为圆形
嘿,针对你在Three.js粒子球体上要把点改成圆形或纹理的需求,我整理了两种直接可行的方案,直接修改片元着色器就能实现:
方案一:渲染平滑圆形粒子
要让每个粒子变成圆润的圆点,核心是根据片元距离点中心的距离来控制透明度,超出半径的部分直接透明。修改你的片元着色器代码如下:
uniform float uTime; varying vec3 vColor; varying vec2 vUv; precision mediump float; void main() { // 计算当前片元到点中心的距离(gl_PointCoord的范围是0-1,中心在(0.5,0.5)) vec2 toCenter = gl_PointCoord - vec2(0.5); float distanceToCenter = length(toCenter); // 定义圆形半径,0.5是填满整个点的大小,调小一点比如0.4会让边缘更柔和 float radius = 0.5; // 用smoothstep做平滑过渡,避免硬边缘锯齿 float alpha = smoothstep(radius, radius - 0.05, distanceToCenter); // 透明度太低的片元直接丢弃,节省性能 if (alpha < 0.1) discard; // 最终颜色结合粒子自身颜色和透明度 gl_FragColor = vec4(vColor, alpha); }
关键细节说明:
gl_PointCoord是Three.js内置的变量,代表当前片元在点精灵里的坐标,范围从(0,0)到(1,1)smoothstep函数能帮你生成平滑的透明度过渡,比直接用step函数的硬边缘好看很多- 用
discard丢弃透明度过低的片元,避免这些无效片元占用渲染资源
方案二:渲染带纹理的粒子
如果要给粒子贴自定义纹理,需要先在JS代码里加载纹理并传给着色器,再修改片元着色器采样纹理:
第一步:在JS中加载并传递纹理
先把纹理加载好,作为uniform变量传给着色器材质:
// 用Three.js的TextureLoader加载纹理,替换成你的纹理路径 const textureLoader = new THREE.TextureLoader(); const particleTexture = textureLoader.load('your-texture.png'); // 创建着色器材质时传递纹理uniform const particleMaterial = new THREE.ShaderMaterial({ uniforms: { uTexture: { value: particleTexture }, uTime: { value: 0 }, // 保留你原本的其他uniform变量 }, vertexShader: 你的顶点着色器代码, fragmentShader: 下面的片元着色器代码, transparent: true, // 必须开启透明,因为纹理大概率有透明通道 // 可选:如果需要粒子叠加效果,开启AdditiveBlending blending: THREE.AdditiveBlending, });
第二步:修改片元着色器采样纹理
uniform sampler2D uTexture; uniform float uTime; varying vec3 vColor; varying vec2 vUv; precision mediump float; void main() { // 用gl_PointCoord作为纹理坐标,采样纹理颜色 vec4 textureColor = texture2D(uTexture, gl_PointCoord); // 纹理透明度过低的片元直接丢弃 if (textureColor.a < 0.1) discard; // 可以把粒子自身颜色和纹理颜色混合,也可以直接用纹理颜色 gl_FragColor = textureColor * vec4(vColor, 1.0); }
额外玩法:给纹理加动画或重复效果
如果想让纹理动起来或者重复显示,可以加几个uniform变量调整:
uniform vec2 uRepeat; // 比如设为vec2(2.0, 2.0)让纹理重复2次 uniform float uTime; void main() { // 让纹理水平滚动,同时重复显示 vec2 textureUv = gl_PointCoord * uRepeat + vec2(uTime * 0.1, 0.0); vec4 textureColor = texture2D(uTexture, textureUv); if (textureColor.a < 0.1) discard; gl_FragColor = textureColor * vec4(vColor, 1.0); }
注意事项
- 不管用哪种方案,都要确保材质开启了
transparent: true,否则透明效果不会生效 - 如果粒子边缘有锯齿,可以尝试开启材质的
alphaTest,或者调整smoothstep的参数让过渡更柔和 - 用纹理的话,尽量选带透明通道的PNG格式,这样纹理的透明部分才能正常显示
内容的提问来源于stack exchange,提问作者Yuri Stiopin
相关产品推荐
相关产品推荐

