mirror of
https://github.com/unanmed/HumanBreak.git
synced 2026-11-03 18:24:30 +08:00
粒子初步
This commit is contained in:
1 parent
4d45553c84
commit
40e20d9b4f
11 files changed
+316
-191
No files matched your search
@@ -0,0 +1,100 @@
|
||||
import { Matrix4 } from '../webgl/martrix';
|
||||
import { Renderer } from './render';
|
||||
|
||||
type Position3D = [number, number, number];
|
||||
|
||||
export class Camera {
|
||||
/** 视图矩阵 */
|
||||
matrix!: Matrix4;
|
||||
/** 绑定的渲染器 */
|
||||
renderer?: Renderer;
|
||||
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.matrix = new Matrix4();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将该摄像机与一个渲染器绑定
|
||||
* @param renderer 渲染器
|
||||
*/
|
||||
bind(renderer: Renderer) {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消与渲染器的绑定
|
||||
*/
|
||||
unbind() {
|
||||
this.renderer = void 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置摄像机的观察方位
|
||||
* @param eye 视点位置
|
||||
* @param at 目标点位置
|
||||
* @param up 上方向
|
||||
*/
|
||||
lookAt(eye: Position3D, at: Position3D, up: Position3D) {
|
||||
this.matrix = this.calLookAt(eye, at, up);
|
||||
}
|
||||
|
||||
/**
|
||||
* 变换摄像机的观察方位
|
||||
* @param eye 视点位置
|
||||
* @param at 目标点位置
|
||||
* @param up 上方向
|
||||
*/
|
||||
transform(eye: Position3D, at: Position3D, up: Position3D) {
|
||||
this.matrix.multipy(this.calLookAt(eye, at, up));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算摄像机变换矩阵
|
||||
* @see https://github.com/bad4iz/cuon-matrix/blob/main/src/Matrix4/Matrix4.ts
|
||||
* @param eye 视点位置
|
||||
* @param at 目标点位置
|
||||
* @param up 上方向
|
||||
* @returns 转换矩阵
|
||||
*/
|
||||
private calLookAt(eye: Position3D, at: Position3D, up: Position3D) {
|
||||
const [eyeX, eyeY, eyeZ] = eye;
|
||||
const [centerX, centerY, centerZ] = at;
|
||||
const [upX, upY, upZ] = up;
|
||||
|
||||
let fx = centerX - eyeX;
|
||||
let fy = centerY - eyeY;
|
||||
let fz = centerZ - eyeZ;
|
||||
|
||||
const rlf = 1 / Math.sqrt(fx * fx + fy * fy + fz * fz);
|
||||
fx *= rlf;
|
||||
fy *= rlf;
|
||||
fz *= rlf;
|
||||
|
||||
let sx = fy * upZ - fz * upY;
|
||||
let sy = fz * upX - fx * upZ;
|
||||
let sz = fx * upY - fy * upX;
|
||||
|
||||
const rls = 1 / Math.sqrt(sx * sx + sy * sy + sz * sz);
|
||||
sx *= rls;
|
||||
sy *= rls;
|
||||
sz *= rls;
|
||||
|
||||
const ux = sy * fz - sz * fy;
|
||||
const uy = sz * fx - sx * fz;
|
||||
const uz = sx * fy - sy * fx;
|
||||
|
||||
const matrix = new Matrix4();
|
||||
|
||||
matrix[0] = [sx, sy, sz, 0];
|
||||
matrix[1] = [ux, uy, uz, 0];
|
||||
matrix[2] = [-fx, -fy, -fz, 0];
|
||||
matrix[4] = [0, 0, 0, 1];
|
||||
|
||||
matrix.translate(-eyeX, -eyeY, -eyeZ);
|
||||
return matrix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { Ticker } from 'mutate-animate';
|
||||
import { has } from '../utils';
|
||||
import { Camera } from './camera';
|
||||
import { Renderer } from './render';
|
||||
|
||||
interface ParticleThreshold {
|
||||
radius: number;
|
||||
color: number;
|
||||
position: number;
|
||||
}
|
||||
|
||||
interface ParticleOne {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
r: number;
|
||||
}
|
||||
|
||||
interface Loc3D extends Loc {
|
||||
z: number;
|
||||
}
|
||||
|
||||
export class Particle {
|
||||
/** 绑定的摄像机 */
|
||||
camera?: Camera;
|
||||
/** 粒子中心位置 */
|
||||
pos: Loc3D;
|
||||
/** 粒子密度 */
|
||||
density: number;
|
||||
/** 渲染器 */
|
||||
renderer?: Renderer;
|
||||
|
||||
/** 需要渲染的粒子列表 */
|
||||
list: ParticleOne[] = [];
|
||||
|
||||
/** 是否需要更新 */
|
||||
private needUpdate: boolean = false;
|
||||
private ticker: Ticker = new Ticker();
|
||||
|
||||
/** 各个属性的阈值 */
|
||||
threshold: ParticleThreshold = {
|
||||
radius: 2,
|
||||
color: 16,
|
||||
position: 50
|
||||
};
|
||||
|
||||
constructor(density: number, x: number, y: number, z: number) {
|
||||
this.pos = { x, y, z };
|
||||
this.density = density;
|
||||
this.ticker.add(this.updateParticleData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置粒子中心的位置
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
*/
|
||||
setPos(x?: number, y?: number): Particle {
|
||||
has(x) && (this.pos.x = x);
|
||||
has(y) && (this.pos.y = y);
|
||||
this.needUpdate = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置粒子的阈值信息
|
||||
* @param data 阈值信息
|
||||
*/
|
||||
setThreshold(data: Partial<ParticleThreshold>): Particle {
|
||||
const { radius, color, position } = data;
|
||||
has(radius) && (this.threshold.radius = radius);
|
||||
has(color) && (this.threshold.radius = color);
|
||||
has(position) && (this.threshold.radius = position);
|
||||
this.needUpdate = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成粒子
|
||||
*/
|
||||
generate() {}
|
||||
|
||||
/**
|
||||
* 添加到一个渲染器上
|
||||
* @param renderer 渲染器
|
||||
*/
|
||||
appendTo(renderer: Renderer) {
|
||||
renderer.addParticle(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前渲染器上移除
|
||||
*/
|
||||
remove() {
|
||||
this.renderer?.removeParticle(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新粒子信息
|
||||
*/
|
||||
update() {
|
||||
this.needUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 每帧执行的粒子更新器
|
||||
*/
|
||||
private updateParticleData() {
|
||||
if (!this.needUpdate) return;
|
||||
this.needUpdate = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { isWebGLSupported } from '../webgl/utils';
|
||||
import { Camera } from './camera';
|
||||
import { Particle } from './particle';
|
||||
|
||||
export class Renderer {
|
||||
/** 粒子列表 */
|
||||
particleList: Particle[] = [];
|
||||
/** 渲染画布 */
|
||||
canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||
/** webgl绘制上下文 */
|
||||
gl: WebGLRenderingContext;
|
||||
/** 绑定的摄像机 */
|
||||
camera?: Camera;
|
||||
/** 缩放比例 */
|
||||
ratio: number = devicePixelRatio;
|
||||
|
||||
constructor() {
|
||||
if (!isWebGLSupported) {
|
||||
throw new Error(`Your service or browser does not support webgl!`);
|
||||
}
|
||||
this.gl = this.canvas.getContext('webgl')!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化粒子画布
|
||||
* @param width 画布宽度
|
||||
* @param height 画布高度
|
||||
*/
|
||||
initCanvas(width: number, height: number) {
|
||||
const ratio = devicePixelRatio;
|
||||
this.ratio = ratio;
|
||||
this.canvas.width = width * ratio;
|
||||
this.canvas.height = height * ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定摄像机
|
||||
* @param camera 摄像机
|
||||
*/
|
||||
bindCamera(camera: Camera) {
|
||||
this.camera = camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消绑定摄像机
|
||||
*/
|
||||
unbindCamera() {
|
||||
this.camera = void 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到一个html元素中
|
||||
* @param ele html元素
|
||||
*/
|
||||
append(ele: HTMLElement) {
|
||||
ele.appendChild(this.canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前html元素中移除
|
||||
*/
|
||||
remove() {
|
||||
this.canvas.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个粒子
|
||||
* @param particle 粒子
|
||||
*/
|
||||
addParticle(particle: Particle) {
|
||||
this.particleList.push(particle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个粒子
|
||||
* @param particle 粒子
|
||||
*/
|
||||
removeParticle(particle: Particle) {
|
||||
const index = this.particleList.findIndex(v => v === particle);
|
||||
if (index === -1) return;
|
||||
this.particleList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user