【Three.js基础学习】13.Particle

本文详细介绍了如何在Three.js中使用PointsMaterial创建粒子系统,包括设置粒子大小、缩放、颜色、纹理,以及如何处理透明度、深度测试和混合模式以改善视觉效果和性能。还探讨了如何实现动画效果和自定义着色器来提高性能。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

课堂知识点

        1.粒子

        2.PointsMaterial 点材质 ; 可以设置点材质的

         size 大小 , sizeAttenuation 设置这个可以 可以实现缩放粒子 近处的大 远处小,设置false 远近都一样大

        3. Points() 点网格 ,和Mesh()类似 参数接收几何体 和 材质

        注意:使用缓冲几何体 你在画面中什么也看不到;可以先从ShpereGeomatry观看一下效果

        4. 想要随机的粒子

        https://www.kenney.nl/assets/particle-pack

        需要Float32Array 获取数组长度 ,由于一个粒子是 x,y,z 数据 因此在数组中占三位

        如果要500个粒子 则 要 500 * 3

        const position = new Float32Array(count * 3)

        这个时候position 就是相当于500 个 粒子的位置信息

        position[i] = Math.random()  // 设置粒子位置的随机

        // 和上一节对比 都是设置属性 位置, 创建缓冲属性 设置位置数据 和 3

        particleGeometry.setAttribute(

            'position',

            new THREE.BufferAttribute(position,3)

        )

        添加纹理

        可以看到粒子生成 ,但是仔细看背景 是有黑色的背景 要解决

        解决方法:

            aplhaMap 属性 设置

            1. aplhaTest = 0.01 // 阿尔法 测试

                但是不是很完美

            2. depthTest = false // 深度测试  它会判断当前粒子前方是否有东西 有测渲染 不然不渲染

             会有问题 ,有其他的颜色 会很糟糕

            3.depthWrite = false // 深度缓冲区

                当绘画这些东西时候 他会判断 在这个物体前还是后 ,会有这些信息

                可以高度网格 不要在深度缓冲区进行绘画

            前三个对于性能不会影响

           

            4.blending 混合  对于性能有点影响

                depthWrite = false

                blending = THREE.AdditiveBlending

       

        设置粒子颜色不同

            1.color 存储不同颜色信息

            2.vertexColors = true  // 通知

            但是particleMaterial.color = new THREE.Color('#ff88cc') 会影响设置的颜色 。有一种混合色的柑橘

       

        动画效果

            控制每个粒子如何做

            关键在于存储的粒子信息  particleGeometry。attribute.position.array

            particleGeometry.attributes.position.needsUpdate = true

            自定义着色器

                更好的性能

            上述进行动画 会有更大的性能消耗 不好

            如果想要正确 必须创建自己的材质 ,创建自己的着色器


一、代码

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import { Mesh } from 'three'


/**
 * Base
 */
// Debug
const gui = new dat.GUI()

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

/**
 * Textures
 */
const textureLoader = new THREE.TextureLoader()
const particlesTexture = textureLoader.load('/textures/particles/2.png')

/* 
*   Particles 粒子
*/
const particleGeometry = new THREE.BufferGeometry(1,32,32)
const count = 50000

const position = new Float32Array(count * 3)
const color = new Float32Array(count * 3)
for(let i=0;i<count * 3;i++){
    position[i] = (Math.random() - 0.5) * 10
    color[i] = Math.random() 
}
particleGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(position,3)
)
particleGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(color,3)
)

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true
particleMaterial.color = new THREE.Color('#ff88cc')
// particleMaterial.map = particlesTexture

particleMaterial.transparent = true
particleMaterial.alphaMap = particlesTexture
// particleMaterial.alphaTest = 0.01
// particleMaterial.depthTest = false
particleMaterial.depthWrite = false
particleMaterial.blending = THREE.AdditiveBlending
particleMaterial.vertexColors = true

const particles = new THREE.Points(
    particleGeometry,
    particleMaterial
)

scene.add(particles)

/* 
    cube
*/
const cube = new THREE.Mesh(
    new THREE.BoxGeometry(),
    new THREE.MeshBasicMaterial()
)
// scene.add(cube)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 3
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update particles
    // particles.rotation.y = elapsedTime * 0.2

    for(let i =0 ;i<count ; i++){
        const i3 = i * 3

        const x = particleGeometry.attributes.position.array[i3] // 粒子 x的位置


        particleGeometry.attributes.position.array[i3 + 1] = Math.sin(elapsedTime + x)
    }
    particleGeometry.attributes.position.needsUpdate = true // 告诉three.js设置好了 要更新

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

二、知识点

1.PointsMaterial 点材质

size 大小 , sizeAttenuation 设置这个可以 可以实现缩放粒子 近处的大 远处小,设置false 远近都一样大
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true

设置颜色和纹理

particleMaterial.color = new THREE.Color('#ff88cc')
particleMaterial.map = particlesTexture

为什么可以生成多个粒子

Float32Array  获取数组长度 由于一个粒子是 x,y,z 数据 因此在数组中占三位

如果要500个粒子 则 要 500 * 3

其中变量position 的位置 可以设置 然后 每三个数组元素组成一个粒子

然后就需要设置几何体的属性 如 颜色 和 位置

particleGeometry.setAttribute(

    'position',

    new THREE.BufferAttribute(position,3)

)

particleGeometry.setAttribute(

    'color',

    new THREE.BufferAttribute(color,3)

)

同样生成不同颜色的粒子也是这样

不过要设置vertexColors = true 通知 不然不会渲染上去

const particleGeometry = new THREE.BufferGeometry(1,32,32)
const count = 50000

const position = new Float32Array(count * 3)
const color = new Float32Array(count * 3)
for(let i=0;i<count * 3;i++){
    position[i] = (Math.random() - 0.5) * 10
    color[i] = Math.random() 
}
particleGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(position,3)
)
particleGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(color,3)
)

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true
particleMaterial.color = new THREE.Color('#ff88cc') // 这里颜色可以去掉 不去掉会影响设置的颜色 ,混合色
particleMaterial.vertexColors = true

让我们再把纹理添加上

仔细看有背景 纯黑色的 ,但是我想要去掉,因为这样效果不是很好

这个时候有很多方法可以去除

解决方法

1.aplhaMap  将纹理设置成阿尔法,同时设置alphaTest   

const particleGeometry = new THREE.BufferGeometry(1,32,32)
const count = 50000

const position = new Float32Array(count * 3)
const color = new Float32Array(count * 3)
for(let i=0;i<count * 3;i++){
    position[i] = (Math.random() - 0.5) * 10
    color[i] = Math.random() 
}
particleGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(position,3)
)
particleGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(color,3)
)

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true
particleMaterial.color = new THREE.Color('#ff88cc')


particleMaterial.transparent = true
particleMaterial.alphaMap = particlesTexture
particleMaterial.alphaTest = 0.01  // 属于缩小 边缘范围 得到类似的效果

 particleMaterial.vertexColors = true

const particles = new THREE.Points(
    particleGeometry,
    particleMaterial
)

scene.add(particles)

效果不是很好 仔细看圆圈重叠处有黑色的边界。

2.同上 不过将阿尔法测试属性 换成 depthTest = false 

particleMaterial.depthTest = false // 深度测试  它会判断当前粒子前方是否有东西 有测渲染 不然不渲染

看起来边缘什么的还可以 ,不过在其他颜色会有问题

可以看到颜色有种透视的感觉!

3.depthWrite 

因此我们可以设置另一个属性深度缓冲区,可以看到在白色后面的颜色没有显示

const particleGeometry = new THREE.BufferGeometry(1,32,32)
const count = 50000

const position = new Float32Array(count * 3)
const color = new Float32Array(count * 3)
for(let i=0;i<count * 3;i++){
    position[i] = (Math.random() - 0.5) * 10
    color[i] = Math.random() 
}
particleGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(position,3)
)
particleGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(color,3)
)

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true
particleMaterial.color = new THREE.Color('#ff88cc')  // 这里可以去掉

particleMaterial.transparent = true
particleMaterial.alphaMap = particlesTexture

/*

  当绘画这些东西时候 他会判断 在这个物体前还是后 ,会有这些信息
                可以高度网格 不要在深度缓冲区进行绘画
*/
particleMaterial.depthWrite = false  // 这里 


const particles = new THREE.Points(
    particleGeometry,
    particleMaterial
)

scene.add(particles)

     前三个对于性能不会影响

4.blending

const particleGeometry = new THREE.BufferGeometry(1,32,32)
const count = 50000

const position = new Float32Array(count * 3)
const color = new Float32Array(count * 3)
for(let i=0;i<count * 3;i++){
    position[i] = (Math.random() - 0.5) * 10
    color[i] = Math.random() 
}
particleGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(position,3)
)
particleGeometry.setAttribute(
    'color',
    new THREE.BufferAttribute(color,3)
)

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.1
particleMaterial.sizeAttenuation = true
particleMaterial.color = new THREE.Color('#ff88cc')

particleMaterial.transparent = true
particleMaterial.alphaMap = particlesTexture

particleMaterial.depthWrite = false  // 这里 结合depthWrite 使用 效果完美 性能可能有问题
particleMaterial.blending = THREE.AdditiveBlending
particleMaterial.vertexColors = true

const particles = new THREE.Points(
    particleGeometry,
    particleMaterial
)

scene.add(particles)

2.实现波浪

下面代码主要是数学,头大了! 结合js 和中设置属性的方法

  const elapsedTime = clock.getElapsedTime()

    // Update particles
    particles.rotation.y = elapsedTime * 0.2

    for(let i =0 ;i<count ; i++){
        const i3 = i * 3

        const x = particleGeometry.attributes.position.array[i3] // 粒子 x的位置

           //让x的位置随时间上下波动
        particleGeometry.attributes.position.array[i3 + 1] = Math.sin(elapsedTime + x)
    }
    particleGeometry.attributes.position.needsUpdate = true // 告诉three.js设置好了 要更新

波动


总结

点点点。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值