上图
着色器设置点材质时,在顶点着色器中,最好设置gl_PointSize,不然看不到你在页面中添加的点
main.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import gsap from 'gsap'
import theVertexShader from './shader/13/vertex.glsl?raw'
import theFragmentShader from './shader/13/fragment.glsl?raw'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerHeight / window.innerHeight, 0.1, 1000)
camera.position.set(0, 0, 5)
camera.aspect = window.innerWidth / window.innerHeight
scene.add(camera)
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)
const textureLoader = new THREE.TextureLoader()
let texture = textureLoader.load('../public/assets/texture/particles/9.png')
let texture1 = textureLoader.load('../public/assets/texture/particles/10.png')
let texture2 = textureLoader.load('../public/assets/texture/particles/11.png')
const params = {
count: 1000,
size: 0.1,
radius: 5,
branch: 4,
color: '#ff6030',
outColor: '#1b3984'
}
let geometry = null
let material = null
let point = null
let galaxyColor = new THREE.Color(params.color)
let outGalaxyColor = new THREE.Color(params.outColor)
const generateGalaxy = () => {
if (point !== null) {
geometry.dispose()
material.dispose()
scene.remove(point)
}
geometry = new THREE.BufferGeometry()
const position = new Float32Array(params.count * 3)
const colors = new Float32Array(params.count * 3)
const imgIndex = new Float32Array(params.count)
const size_arr = new Float32Array(params.count)
for (let i = 0; i < params.count; i++) {
const current = i * 3
const branchAngel = (i % params.branch) * ((2 * Math.PI) / params.branch)
const distance = Math.random() * params.radius
const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5
position[current] = Math.cos(branchAngel) * distance + randomX
position[current + 1] = 0 + randomY
position[current + 2] = Math.sin(branchAngel) * distance + randomZ
const mixColor = galaxyColor.clone()
mixColor.lerp(outGalaxyColor, distance / params.radius)
colors[current] = mixColor.r
colors[current + 1] = mixColor.g
colors[current + 2] = mixColor.b
imgIndex[current] = i % 3
size_arr[current] = Math.random()
}
geometry.setAttribute('position', new THREE.BufferAttribute(position, 3))
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
geometry.setAttribute('imgIndex', new THREE.BufferAttribute(imgIndex, 1))
geometry.setAttribute('asize', new THREE.BufferAttribute(size_arr, 1))
material = new THREE.ShaderMaterial({
vertexShader: theVertexShader,
fragmentShader: theFragmentShader,
transparent: true,
vertexColors: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
uniforms: {
uTime: {
value: 0
},
uTexture: {
value: texture
},
uTexture1: {
value: texture1
},
uTexture2: {
value: texture2
},
uColor: {
value: galaxyColor
}
}
})
point = new THREE.Points(geometry, material)
scene.add(point)
}
generateGalaxy()
const renderer = new THREE.WebGLRenderer()
renderer.shadowMap.enabled = true
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.01
const clock = new THREE.Clock()
function animate() {
const elapsedTime = clock.getElapsedTime()
material.uniforms.uTime.value = elapsedTime
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
animate()
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
})
vertex.glsl
precision lowp float;
varying vec2 vUv;
attribute float asize;
attribute float imgIndex;
varying float vImgIndex;
uniform float uTime;
varying vec3 vColor;
void main() {
vUv = uv;
vColor = color;
vImgIndex = imgIndex;
vec4 modelPosition = modelMatrix*vec4(position, 1.0);
`获取顶点的角度
注:【atan(y, x)】计算两点之间的角度(以弧度为单位)`
float angle = atan(modelPosition.x, modelPosition.z);
`获取顶点到中心的距离
注:
按道理得这么写:【length(vec2(modelPosition.x, modelPosition.z))】
如果 modelPosition 是一个三维向量,并且,你只需要 x 和 z 分量,length(modelPosition.xz)这样写也可以`
float distanceToCenter = length(modelPosition.xz);
`角度偏移angleOffset,是基于,顶点到中心距离的倒数,乘以时间uTime来计算的
这种方法会导致:
当顶点接近中心时,偏移量变得非常大,导致快速旋转`
float angleOffset = 1.0/distanceToCenter*uTime;
`当前旋转的度数`
angle = angle+angleOffset;
modelPosition.x = cos(angle)*distanceToCenter;
modelPosition.z = sin(angle)*distanceToCenter;
vec4 viewPosition = viewMatrix*modelPosition;
gl_Position = projectionMatrix*viewPosition;
`不设置 gl_PointSize 页面上就啥也没有
1、GLSL中,`gl_PointSize`是一个特殊的输出变量,用于,设置点渲染时点的大小,
2、以【gl_PointSize = 200.0/-viewPosition.z*asize】为例:
涉及,视点空间中,顶点的z坐标(viewPosition.z),和一个属性asize的值`
gl_PointSize = 200.0/-viewPosition.z*asize;
}
fragment.glsl
precision lowp float;
uniform sampler2D uTexture;
uniform sampler2D uTexture1;
uniform sampler2D uTexture2;
varying float vImgIndex;
varying vec3 vColor;
void main() {
vec4 textureColor;
if(vImgIndex==0.0) {
textureColor = texture2D(uTexture, gl_PointCoord);
} else if(vImgIndex==1.0) {
textureColor = texture2D(uTexture1, gl_PointCoord);
} else {
textureColor = texture2D(uTexture2, gl_PointCoord);
}
gl_FragColor = vec4(vColor, textureColor.r);
}