【Three.js基础学习】26. Animated galaxy
前言
shaders实现星系
课程回顾
使用顶点着色器为每个粒子设置动画
a属性 , u制服 ,v变化
像素比:window.devicePixelRatio
自动从渲染器检索像素比
renderer.getPixelRatio()
如何尺寸衰减, 放大缩小视角时,粒子都是同样大小的问题
gl_Position += (1.0 / - viewPosition);
下面是实现的数学解释
一、代码
这个我听的迷迷糊糊的,直接看代码中的注释吧
1.script.js
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'lil-gui'
import galaxyVertexShader from './shaders/galaxy/vertex.glsl'
import galaxyFragmentShader from './shaders/galaxy/fragment.glsl'
/**
* Base
*/
// Debug
const gui = new dat.GUI()
// Canvas
const canvas = document.querySelector('canvas.webgl')
// Scene
const scene = new THREE.Scene()
/**
* Galaxy
*/
const parameters = {}
parameters.count = 200000
parameters.size = 0.005
parameters.radius = 6
parameters.branches = 3
parameters.spin = 1
parameters.randomness = 0.2
parameters.randomnessPower = 3
parameters.insideColor = '#ff6030'
parameters.outsideColor = '#1b3984'
let geometry = null
let material = null
let points = null
const generateGalaxy = () =>
{
if(points !== null) // 制空
{
geometry.dispose()
material.dispose()
scene.remove(points)
}
/**
* Geometry
*/
geometry = new THREE.BufferGeometry()
// 这里为什么*3 因为要向各个方向发展,为什么*1只是在一个方向上拉伸
const positions = new Float32Array(parameters.count * 3)
const randomness = new Float32Array(parameters.count * 3)
const colors = new Float32Array(parameters.count * 3)
const scales = new Float32Array(parameters.count * 1)
const insideColor = new THREE.Color(parameters.insideColor)
const outsideColor = new THREE.Color(parameters.outsideColor)
for(let i = 0; i < parameters.count; i++)
{
const i3 = i * 3
// Position
const radius = Math.random() * parameters.radius
const branchAngle = (i % parameters.branches) / parameters.branches * Math.PI * 2
/*
长时间就会发现星系在变薄,像一条丝带 ,应该避免
下面代码 创建一个新的Float32Array并且在旋转后使用该属性 不应该在旋转之前添加应用随机性randomZXY
*/
const randomZ = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : - 1) * parameters.randomness * radius
const randomX = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : - 1) * parameters.randomness * radius
const randomY = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : - 1) * parameters.randomness * radius
// 旋转 将数据填充到位置上
positions[i3 ] = Math.cos(branchAngle) * radius
positions[i3 + 1] = 0.0
positions[i3 + 2] = Math.sin(branchAngle) * radius
randomness[i3 ] = randomX
randomness[i3 + 1] = randomY
randomness[i3 + 2] = randomZ
// Color
const mixedColor = insideColor.clone()
mixedColor.lerp(outsideColor, radius / parameters.radius)
colors[i3 ] = mixedColor.r
colors[i3 + 1] = mixedColor.g
colors[i3 + 2] = mixedColor.b
// Scale
scales[i] = Math.random()
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
geometry.setAttribute('aRandomness', new THREE.BufferAttribute(randomness, 3))
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))
geometry.setAttribute('aScale', new THREE.BufferAttribute(scales, 1))
/**
* Material
*/
material = new THREE.ShaderMaterial({
depthWrite: false,
blending: THREE.AdditiveBlending,
vertexColors: true,
uniforms:
{
uTime: { value: 0 },
uSize: { value: 30 * renderer.getPixelRatio() } // 星系顶点尺寸*像素比
},
vertexShader: galaxyVertexShader,
fragmentShader: galaxyFragmentShader
})
/**
* Points
*/
points = new THREE.Points(geometry, material)
scene.add(points)
}
gui.add(parameters, 'count').min(100).max(1000000).step(100).onFinishChange(generateGalaxy).name('数量')
gui.add(parameters, 'radius').min(0.01).max(20).step(0.01).onFinishChange(generateGalaxy).name('半径')
gui.add(parameters, 'branches').min(2).max(20).step(1).onFinishChange(generateGalaxy).name('条数')
gui.add(parameters, 'randomness').min(0).max(2).step(0.001).onFinishChange(generateGalaxy).name('随机')
gui.add(parameters, 'randomnessPower').min(1).max(10).step(0.001).onFinishChange(generateGalaxy).name('随机功率')
gui.addColor(parameters, 'insideColor').onFinishChange(generateGalaxy).name('内部颜色')
gui.addColor(parameters, 'outsideColor').onFinishChange(generateGalaxy).name('外部颜色')
/**
* 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.x = 3
camera.position.y = 3
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))
/**
* Generate the first galaxy
*/
generateGalaxy()
/**
* Animate
*/
const clock = new THREE.Clock()
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
// Update material
material.uniforms.uTime.value = elapsedTime
// Update controls
controls.update()
// Render
renderer.render(scene, camera)
// Call tick again on the next frame
window.requestAnimationFrame(tick)
}
tick()
2. 顶点着色器
uniform float uTime;
uniform float uSize;
attribute vec3 aRandomness;
attribute float aScale;
varying vec3 vColor;
/*
atan(x) 弧度 反正切函数
*/
void main()
{
/**
* Position
*/
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
// Rotate
float angle = atan(modelPosition.x, modelPosition.z); // 获取角度 偏移的
float distanceToCenter = length(modelPosition.xz); // 到中心的距离
float angleOffset = (1.0 / distanceToCenter) * uTime ; // 根据时间应该旋转多少度
angle += angleOffset; // 更新角度 并且 角度加上角度偏移
modelPosition.x = cos(angle) * distanceToCenter; // 更新到模型上,同时乘以到中心的距离
modelPosition.z = sin(angle) * distanceToCenter;
// Randomness 随机
modelPosition.xyz += aRandomness;
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;
gl_Position = projectedPosition;
/**
* Size
*/
gl_PointSize = uSize * aScale;
gl_PointSize *= (1.0 / - viewPosition.z);
/**
* Color
*/
vColor = color;
}
3.片段着色器
varying vec3 vColor;
void main()
{
// // Disc
// float strength = distance(gl_PointCoord, vec2(0.5));
// strength = step(0.5, strength);
// strength = 1.0 - strength;
// // Diffuse point
// float strength = distance(gl_PointCoord, vec2(0.5));
// strength *= 2.0;
// strength = 1.0 - strength;
// Light point
float strength = distance(gl_PointCoord, vec2(0.5));
strength = 1.0 - strength;
strength = pow(strength, 10.0);
// Final color
vec3 color = mix(vec3(0.0), vColor, strength);
gl_FragColor = vec4(color, 1.0);
}
二、效果
shaders - 星系
总结
今天学的迷迷糊糊的,整体代码知道看得懂,思路,如何偏移以及一些数学计算很蒙!