mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-03-01 01:57:05 +08:00
feat: 下雨天气
This commit is contained in:
parent
b599f9f146
commit
5918bfcf61
@ -12,7 +12,7 @@ precision highp float;
|
|||||||
in vec4 a_position;
|
in vec4 a_position;
|
||||||
in vec2 a_texCoord;
|
in vec2 a_texCoord;
|
||||||
|
|
||||||
out highp vec2 v_texCoord;
|
out vec2 v_texCoord;
|
||||||
`;
|
`;
|
||||||
const SHADER_VERTEX_PREFIX_100 = /* glsl */ `
|
const SHADER_VERTEX_PREFIX_100 = /* glsl */ `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
@ -20,20 +20,20 @@ precision highp float;
|
|||||||
attribute vec4 a_position;
|
attribute vec4 a_position;
|
||||||
attribute vec2 a_texCoord;
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
varying highp vec2 v_texCoord;
|
varying vec2 v_texCoord;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SHADER_FRAGMENT_PREFIX_300 = /* glsl */ `#version 300 es
|
const SHADER_FRAGMENT_PREFIX_300 = /* glsl */ `#version 300 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
in highp vec2 v_texCoord;
|
in vec2 v_texCoord;
|
||||||
|
|
||||||
uniform sampler2D u_sampler;
|
uniform sampler2D u_sampler;
|
||||||
`;
|
`;
|
||||||
const SHADER_FRAGMENT_PREFIX_100 = /* glsl */ `
|
const SHADER_FRAGMENT_PREFIX_100 = /* glsl */ `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
varying highp vec2 v_texCoord;
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
uniform sampler2D u_sampler;
|
uniform sampler2D u_sampler;
|
||||||
`;
|
`;
|
||||||
@ -268,9 +268,7 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update(item?: RenderItem<any>): void {
|
update(item?: RenderItem<any>): void {
|
||||||
const isSelf = item === this && !this.cacheDirty;
|
|
||||||
super.update(item);
|
super.update(item);
|
||||||
if (isSelf) this.cacheDirty = false;
|
|
||||||
this.shaderRenderDirty = true;
|
this.shaderRenderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +281,6 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
const ready = dr && program.ready();
|
const ready = dr && program.ready();
|
||||||
if (!ready) return;
|
if (!ready) return;
|
||||||
const indices = program.usingIndices;
|
const indices = program.usingIndices;
|
||||||
if (!indices) return;
|
|
||||||
const param = program.getDrawParams(program.renderMode);
|
const param = program.getDrawParams(program.renderMode);
|
||||||
if (!param) return;
|
if (!param) return;
|
||||||
|
|
||||||
@ -293,22 +290,22 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
gl.clearDepth(1);
|
gl.clearDepth(1);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
const pre = this.preDraw();
|
const pre = this.preDraw(gl, program, param, indices);
|
||||||
if (!pre) {
|
if (!pre) {
|
||||||
this.postDraw();
|
this.postDraw(gl, program, param, indices);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.draw(gl, program, param, indices);
|
this.draw(gl, program, param, indices);
|
||||||
|
|
||||||
this.postDraw();
|
this.postDraw(gl, program, param, indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
private draw(
|
draw(
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
program: ShaderProgram,
|
program: ShaderProgram,
|
||||||
param: DrawParamsMap[keyof DrawParamsMap],
|
param: DrawParamsMap[keyof DrawParamsMap],
|
||||||
indices: IShaderIndices
|
indices: IShaderIndices | null
|
||||||
) {
|
) {
|
||||||
switch (program.renderMode) {
|
switch (program.renderMode) {
|
||||||
case RenderMode.Arrays: {
|
case RenderMode.Arrays: {
|
||||||
@ -316,6 +313,7 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
gl.drawArrays(mode, first, count);
|
gl.drawArrays(mode, first, count);
|
||||||
}
|
}
|
||||||
case RenderMode.Elements: {
|
case RenderMode.Elements: {
|
||||||
|
if (!indices) return;
|
||||||
const { mode, count, type, offset } =
|
const { mode, count, type, offset } =
|
||||||
param as DrawElementsParam;
|
param as DrawElementsParam;
|
||||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices.data);
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices.data);
|
||||||
@ -327,6 +325,7 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
gl.drawArraysInstanced(mode, first, count, instanceCount);
|
gl.drawArraysInstanced(mode, first, count, instanceCount);
|
||||||
}
|
}
|
||||||
case RenderMode.ElementsInstanced: {
|
case RenderMode.ElementsInstanced: {
|
||||||
|
if (!indices) return;
|
||||||
const {
|
const {
|
||||||
mode,
|
mode,
|
||||||
count,
|
count,
|
||||||
@ -344,7 +343,12 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
* 在本着色器内部渲染之前执行的渲染,如果返回false,则表示不进行内部渲染,但依然会执行 {@link postDraw}。
|
* 在本着色器内部渲染之前执行的渲染,如果返回false,则表示不进行内部渲染,但依然会执行 {@link postDraw}。
|
||||||
* 继承本类,并复写此方法即可实现前置渲染功能
|
* 继承本类,并复写此方法即可实现前置渲染功能
|
||||||
*/
|
*/
|
||||||
protected preDraw(): boolean {
|
protected preDraw(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
program: ShaderProgram,
|
||||||
|
param: DrawParamsMap[keyof DrawParamsMap],
|
||||||
|
indices: IShaderIndices | null
|
||||||
|
): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,9 +356,18 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
* 在本着色器内部渲染之后执行的渲染,即使preDraw返回false,本函数也会执行
|
* 在本着色器内部渲染之后执行的渲染,即使preDraw返回false,本函数也会执行
|
||||||
* 继承本类,并复写此方法即可实现后置渲染功能
|
* 继承本类,并复写此方法即可实现后置渲染功能
|
||||||
*/
|
*/
|
||||||
protected postDraw() {}
|
protected postDraw(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
program: ShaderProgram,
|
||||||
|
param: DrawParamsMap[keyof DrawParamsMap],
|
||||||
|
indices: IShaderIndices | null
|
||||||
|
) {}
|
||||||
|
|
||||||
private defaultReady(): boolean {
|
/**
|
||||||
|
* 默认的准备函数
|
||||||
|
* @returns 是否准备成功
|
||||||
|
*/
|
||||||
|
protected defaultReady(): boolean {
|
||||||
const program = this.program;
|
const program = this.program;
|
||||||
if (!program) return false;
|
if (!program) return false;
|
||||||
const tex = program.getTexture('u_sampler');
|
const tex = program.getTexture('u_sampler');
|
||||||
@ -491,6 +504,8 @@ export class Shader extends Container<EShaderEvent> {
|
|||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
if (!gl) return;
|
if (!gl) return;
|
||||||
gl.enable(gl.DEPTH_TEST);
|
gl.enable(gl.DEPTH_TEST);
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
gl.depthFunc(gl.LEQUAL);
|
gl.depthFunc(gl.LEQUAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,7 +559,7 @@ interface AttribSetFn {
|
|||||||
[AttribType.AttribI4uiv]: _A<Uint32List>;
|
[AttribType.AttribI4uiv]: _A<Uint32List>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderUniform<T extends UniformType> {
|
export interface IShaderUniform<T extends UniformType> {
|
||||||
/** 这个 uniform 变量的内存位置 */
|
/** 这个 uniform 变量的内存位置 */
|
||||||
readonly location: WebGLUniformLocation;
|
readonly location: WebGLUniformLocation;
|
||||||
/** 这个 uniform 变量的类型 */
|
/** 这个 uniform 变量的类型 */
|
||||||
@ -559,7 +574,7 @@ interface IShaderUniform<T extends UniformType> {
|
|||||||
set(...params: UniformSetFn[T]): void;
|
set(...params: UniformSetFn[T]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderAttrib<T extends AttribType> {
|
export interface IShaderAttrib<T extends AttribType> {
|
||||||
/** 这个 attribute 常量的内存位置 */
|
/** 这个 attribute 常量的内存位置 */
|
||||||
readonly location: number;
|
readonly location: number;
|
||||||
/** 这个 attribute 常量的类型 */
|
/** 这个 attribute 常量的类型 */
|
||||||
@ -575,7 +590,7 @@ interface IShaderAttrib<T extends AttribType> {
|
|||||||
set(...params: AttribSetFn[T]): void;
|
set(...params: AttribSetFn[T]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderAttribArray {
|
export interface IShaderAttribArray {
|
||||||
/** 这个 attribute 常量的内存位置 */
|
/** 这个 attribute 常量的内存位置 */
|
||||||
readonly location: number;
|
readonly location: number;
|
||||||
/** 这个 attribute 所用的缓冲区信息 */
|
/** 这个 attribute 所用的缓冲区信息 */
|
||||||
@ -670,7 +685,7 @@ interface IShaderAttribArray {
|
|||||||
disable(): void;
|
disable(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderIndices {
|
export interface IShaderIndices {
|
||||||
/** 这个顶点索引所用的缓冲区信息 */
|
/** 这个顶点索引所用的缓冲区信息 */
|
||||||
readonly data: WebGLBuffer;
|
readonly data: WebGLBuffer;
|
||||||
/** 这个量所处的着色器程序 */
|
/** 这个量所处的着色器程序 */
|
||||||
@ -719,7 +734,7 @@ interface IShaderIndices {
|
|||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderUniformMatrix {
|
export interface IShaderUniformMatrix {
|
||||||
/** 矩阵的内存位置 */
|
/** 矩阵的内存位置 */
|
||||||
readonly location: WebGLUniformLocation;
|
readonly location: WebGLUniformLocation;
|
||||||
/** 矩阵类型 */
|
/** 矩阵类型 */
|
||||||
@ -741,7 +756,7 @@ interface IShaderUniformMatrix {
|
|||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderUniformBlock {
|
export interface IShaderUniformBlock {
|
||||||
/** 这个 uniform block 的内存地址 */
|
/** 这个 uniform block 的内存地址 */
|
||||||
readonly location: GLuint;
|
readonly location: GLuint;
|
||||||
/** 与这个 uniform block 所绑定的缓冲区 */
|
/** 与这个 uniform block 所绑定的缓冲区 */
|
||||||
@ -764,7 +779,7 @@ interface IShaderUniformBlock {
|
|||||||
set(srcData: ArrayBufferView, srcOffset: number, length?: number): void;
|
set(srcData: ArrayBufferView, srcOffset: number, length?: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShaderTexture2D {
|
export interface IShaderTexture2D {
|
||||||
/** 纹理对象 */
|
/** 纹理对象 */
|
||||||
readonly texture: WebGLTexture;
|
readonly texture: WebGLTexture;
|
||||||
/** 宽度 */
|
/** 宽度 */
|
||||||
@ -1235,7 +1250,7 @@ interface DrawElementsInstancedParam {
|
|||||||
instanceCount: number;
|
instanceCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DrawParamsMap {
|
export interface DrawParamsMap {
|
||||||
[RenderMode.Arrays]: DrawArraysParam;
|
[RenderMode.Arrays]: DrawArraysParam;
|
||||||
[RenderMode.ArraysInstanced]: DrawArraysInstancedParam;
|
[RenderMode.ArraysInstanced]: DrawArraysInstancedParam;
|
||||||
[RenderMode.Elements]: DrawElementsParam;
|
[RenderMode.Elements]: DrawElementsParam;
|
||||||
@ -1684,6 +1699,7 @@ export class ShaderProgram extends EventEmitter<ShaderProgramEvent> {
|
|||||||
const buffer = gl.createBuffer();
|
const buffer = gl.createBuffer();
|
||||||
if (!buffer) return null;
|
if (!buffer) return null;
|
||||||
const location = gl.getAttribLocation(program, name);
|
const location = gl.getAttribLocation(program, name);
|
||||||
|
if (location === -1) return null;
|
||||||
const obj = new ShaderAttribArray(buffer, location, gl, this);
|
const obj = new ShaderAttribArray(buffer, location, gl, this);
|
||||||
this.attribArray.set(name, obj);
|
this.attribArray.set(name, obj);
|
||||||
return obj;
|
return obj;
|
||||||
@ -1870,7 +1886,7 @@ export class ShaderProgram extends EventEmitter<ShaderProgramEvent> {
|
|||||||
const sampler = this.defineTexture('u_sampler', 0);
|
const sampler = this.defineTexture('u_sampler', 0);
|
||||||
const indices = this.defineIndices('defalutIndices');
|
const indices = this.defineIndices('defalutIndices');
|
||||||
if (!tex || !position || !sampler || !indices) {
|
if (!tex || !position || !sampler || !indices) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
position.buffer(
|
position.buffer(
|
||||||
new Float32Array([1, -1, -1, -1, 1, 1, -1, 1]),
|
new Float32Array([1, -1, -1, -1, 1, 1, -1, 1]),
|
||||||
|
250
src/module/weather/rain.ts
Normal file
250
src/module/weather/rain.ts
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
import {
|
||||||
|
DrawParamsMap,
|
||||||
|
IShaderIndices,
|
||||||
|
IShaderUniform,
|
||||||
|
Shader,
|
||||||
|
ShaderProgram,
|
||||||
|
UniformType
|
||||||
|
} from '@/core/render/shader';
|
||||||
|
import { IWeather, WeatherController } from './weather';
|
||||||
|
import { MotaRenderer } from '@/core/render/render';
|
||||||
|
import { Container } from '@/core/render/container';
|
||||||
|
|
||||||
|
const rainVs = /* glsl */ `
|
||||||
|
in vec2 a_rainVertex;
|
||||||
|
in vec2 a_offset; // 雨滴的中心位置
|
||||||
|
in vec4 a_data; // x: 雨滴宽度; y: 雨滴长度; z: 雨滴旋转角度,0表示向下,逆时针为正;
|
||||||
|
// w: 属于哪一种雨,需要两种雨反复循环,一种无法实现循环,0表示第一种,1表示第二种
|
||||||
|
|
||||||
|
uniform float u_progress; // 下雨进度,从最上落到最下是0.5个进度,以保证不会出现奇怪的问题
|
||||||
|
|
||||||
|
out vec2 v_center;
|
||||||
|
out vec2 v_data; // 雨滴宽度与高度
|
||||||
|
out vec2 v_pos;
|
||||||
|
|
||||||
|
mat2 createScaleMatrix(float x, float y) {
|
||||||
|
return mat2(
|
||||||
|
x, 0,
|
||||||
|
0, y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getOffsetByProgress(vec2 offset) {
|
||||||
|
if (a_data.w == 0.0) {
|
||||||
|
if (u_progress < 0.5) {
|
||||||
|
return offset * u_progress * 2.0;
|
||||||
|
} else {
|
||||||
|
return offset * (u_progress - 1.0) * 2.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return offset * u_progress * 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float cosTheta = cos(a_data.z);
|
||||||
|
float sinTheta = sin(a_data.z);
|
||||||
|
mat2 rotate = mat2(
|
||||||
|
cosTheta, -sinTheta,
|
||||||
|
sinTheta, cosTheta
|
||||||
|
);
|
||||||
|
vec2 offset = getOffsetByProgress(vec2(-sinTheta * 2.0, -cosTheta * 2.0));
|
||||||
|
mat2 scale = createScaleMatrix(a_data.x, a_data.y);
|
||||||
|
vec2 off = a_offset + offset;
|
||||||
|
vec2 pos = rotate * scale * a_rainVertex + off;
|
||||||
|
v_center = off;
|
||||||
|
v_pos = pos;
|
||||||
|
gl_Position = vec4(pos, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const rainFs = /* glsl */ `
|
||||||
|
in vec2 v_center;
|
||||||
|
in vec2 v_data; // 雨滴的宽度与长度
|
||||||
|
in vec2 v_pos;
|
||||||
|
|
||||||
|
uniform vec4 u_color; // 雨滴的颜色
|
||||||
|
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
float random(vec2 uv) {
|
||||||
|
return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float dis = distance(v_pos, v_center);
|
||||||
|
float alpha = 1.0;
|
||||||
|
float decay = v_data.y * 0.5 * 0.5;
|
||||||
|
if (dis > decay) {
|
||||||
|
alpha = 2.0 * (dis - decay) / decay;
|
||||||
|
}
|
||||||
|
float ran = random(v_pos);
|
||||||
|
vec2 pos = vec2(v_pos.x + ran * 0.01, v_pos.y);
|
||||||
|
vec2 texPos = (pos + 1.0) / 2.0;
|
||||||
|
texPos.y = 1.0 - texPos.y;
|
||||||
|
vec4 tex = texture(u_sampler, texPos);
|
||||||
|
outColor = mix(u_color, tex, 0.9);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** 雨滴顶点坐标 */
|
||||||
|
const vertex = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
|
||||||
|
|
||||||
|
Mota.require('var', 'loading').once('coreInit', () => {
|
||||||
|
const shader = new RainShader();
|
||||||
|
const gl = shader.gl;
|
||||||
|
shader.size(480, 480);
|
||||||
|
shader.setHD(true);
|
||||||
|
RainWeather.shader = shader;
|
||||||
|
const program = shader.createProgram();
|
||||||
|
program.setVersion(shader.VERSION_ES_300);
|
||||||
|
program.fs(rainFs);
|
||||||
|
program.vs(rainVs);
|
||||||
|
program.requestCompile();
|
||||||
|
program.useDefault(false);
|
||||||
|
const pos = program.defineAttribArray('a_rainVertex');
|
||||||
|
program.defineAttribArray('a_offset');
|
||||||
|
program.defineAttribArray('a_data');
|
||||||
|
program.defineUniform('u_progress', shader.UNIFORM_1f);
|
||||||
|
program.defineUniform('u_color', shader.UNIFORM_4f);
|
||||||
|
program.mode(shader.DRAW_ARRAYS_INSTANCED);
|
||||||
|
RainShader.rainProgram = program;
|
||||||
|
shader.useProgram(program);
|
||||||
|
|
||||||
|
if (pos) {
|
||||||
|
pos.buffer(vertex, gl.STATIC_DRAW);
|
||||||
|
pos.pointer(2, gl.FLOAT, false, 0, 0);
|
||||||
|
pos.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
const back = shader.createProgram();
|
||||||
|
back.requestCompile();
|
||||||
|
back.modified = true;
|
||||||
|
back.useDefault(false);
|
||||||
|
RainShader.backProgram = back;
|
||||||
|
shader.useProgram(back);
|
||||||
|
});
|
||||||
|
|
||||||
|
export class RainWeather implements IWeather {
|
||||||
|
static id: string = 'rain';
|
||||||
|
|
||||||
|
static shader: RainShader;
|
||||||
|
|
||||||
|
private progress: IShaderUniform<UniformType.Uniform1f> | null = null;
|
||||||
|
|
||||||
|
constructor(readonly level: number = 5) {}
|
||||||
|
|
||||||
|
activate(): void {
|
||||||
|
const render = MotaRenderer.get('render-main');
|
||||||
|
const layer = render?.getElementById('layer-main');
|
||||||
|
const draw = render?.getElementById('map-draw') as Container;
|
||||||
|
if (!layer || !draw) return;
|
||||||
|
const shader = RainWeather.shader;
|
||||||
|
layer.append(shader);
|
||||||
|
shader.append(draw);
|
||||||
|
|
||||||
|
const gl = shader.gl;
|
||||||
|
const program = RainShader.rainProgram;
|
||||||
|
program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, 100 * this.level);
|
||||||
|
|
||||||
|
this.progress = program.getUniform<UniformType.Uniform1f>('u_progress');
|
||||||
|
shader.generateRainPath(
|
||||||
|
this.level * 100,
|
||||||
|
(((Math.random() - 0.5) * Math.PI) / 30) * this.level,
|
||||||
|
(Math.PI / 180) * (12 - this.level)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
frame(): void {
|
||||||
|
RainWeather.shader.update(RainWeather.shader);
|
||||||
|
const time = 5000 - 400 * this.level;
|
||||||
|
const progress = (Date.now() % time) / time;
|
||||||
|
// console.log(progress);
|
||||||
|
|
||||||
|
RainWeather.shader.useProgram(RainShader.rainProgram);
|
||||||
|
this.progress?.set(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate(): void {
|
||||||
|
const render = MotaRenderer.get('render-main');
|
||||||
|
const layer = render?.getElementById('layer-main');
|
||||||
|
const draw = render?.getElementById('map-draw') as Container;
|
||||||
|
if (!layer || !draw) return;
|
||||||
|
const shader = RainWeather.shader;
|
||||||
|
layer.append(draw);
|
||||||
|
shader.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WeatherController.register(RainWeather);
|
||||||
|
|
||||||
|
class RainShader extends Shader {
|
||||||
|
static rainProgram: ShaderProgram;
|
||||||
|
static backProgram: ShaderProgram;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成雨滴
|
||||||
|
* @param num 雨滴数量
|
||||||
|
*/
|
||||||
|
generateRainPath(num: number, angle: number, deviation: number) {
|
||||||
|
const program = RainShader.rainProgram;
|
||||||
|
RainWeather.shader.useProgram(program);
|
||||||
|
const aOffset = program.getAttribArray('a_offset');
|
||||||
|
const aData = program.getAttribArray('a_data');
|
||||||
|
const color = program.getUniform<UniformType.Uniform4f>('u_color');
|
||||||
|
const gl = this.gl;
|
||||||
|
if (!aOffset || !aData) return;
|
||||||
|
|
||||||
|
const tan = Math.tan(angle);
|
||||||
|
|
||||||
|
const offset = new Float32Array(num * 2);
|
||||||
|
const data = new Float32Array(num * 4);
|
||||||
|
const half = num / 2;
|
||||||
|
for (let i = 0; i < half; i++) {
|
||||||
|
const ox = Math.random() * 3 - 1.5;
|
||||||
|
const oy = Math.random() * 2 - 1;
|
||||||
|
const rad = angle + (Math.random() - 0.5) * Math.PI * deviation;
|
||||||
|
const length = Math.random() * 0.05 + 0.03;
|
||||||
|
const width = Math.random() * 0.002 + 0.002;
|
||||||
|
offset.set([ox, oy], i * 2);
|
||||||
|
data.set([width, length, rad, 0], i * 4);
|
||||||
|
}
|
||||||
|
for (let i = half; i < num; i++) {
|
||||||
|
const ox = Math.random() * 3 - 1.5 + tan * 2;
|
||||||
|
const oy = Math.random() * 2 + 1;
|
||||||
|
const rad = angle + (Math.random() - 0.5) * Math.PI * deviation;
|
||||||
|
const length = Math.random() * 0.1 + 0.05;
|
||||||
|
const width = Math.random() * 0.002 + 0.002;
|
||||||
|
offset.set([ox, oy], i * 2);
|
||||||
|
data.set([width, length, rad, 1], i * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
aOffset.buffer(offset, gl.STATIC_DRAW);
|
||||||
|
aData.buffer(data, gl.STATIC_DRAW);
|
||||||
|
aOffset.pointer(2, gl.FLOAT, false, 0, 0);
|
||||||
|
aOffset.divisor(1);
|
||||||
|
aOffset.enable();
|
||||||
|
aData.pointer(4, gl.FLOAT, false, 0, 0);
|
||||||
|
aData.divisor(1);
|
||||||
|
aData.enable();
|
||||||
|
|
||||||
|
program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, num);
|
||||||
|
color?.set(1, 1, 1, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override preDraw(gl: WebGL2RenderingContext): boolean {
|
||||||
|
const back = RainShader.backProgram;
|
||||||
|
const rain = RainShader.rainProgram;
|
||||||
|
this.useProgram(back);
|
||||||
|
const ready = this.defaultReady();
|
||||||
|
const param = back.getDrawParams(this.DRAW_ELEMENTS);
|
||||||
|
const rainParam = rain.getDrawParams(this.DRAW_ARRAYS_INSTANCED);
|
||||||
|
if (!ready || !param || !rainParam) return false;
|
||||||
|
|
||||||
|
this.draw(gl, back, param, back.usingIndices);
|
||||||
|
this.useProgram(rain);
|
||||||
|
this.draw(gl, rain, rainParam, null);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
75
src/module/weather/weather.ts
Normal file
75
src/module/weather/weather.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { logger } from '@/core/common/logger';
|
||||||
|
import { Ticker } from 'mutate-animate';
|
||||||
|
|
||||||
|
export interface IWeather {
|
||||||
|
/**
|
||||||
|
* 初始化天气,当天气被添加时会被立刻调用
|
||||||
|
*/
|
||||||
|
activate(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每帧执行的函数
|
||||||
|
*/
|
||||||
|
frame(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摧毁这个天气,当天气被删除时会执行
|
||||||
|
*/
|
||||||
|
deactivate(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Weather {
|
||||||
|
id: string;
|
||||||
|
new (level?: number): IWeather;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WeatherController {
|
||||||
|
static list: Map<string, Weather> = new Map();
|
||||||
|
|
||||||
|
/** 当前的所有天气 */
|
||||||
|
active: Set<IWeather> = new Set();
|
||||||
|
ticker: Ticker = new Ticker();
|
||||||
|
|
||||||
|
private tick = () => {
|
||||||
|
this.active.forEach(v => {
|
||||||
|
v.frame();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个天气,如果天气不存在则抛出警告。注意虽然原则上允许天气重复,但一些天气在实现时,并不允许重复
|
||||||
|
* @param id 天气的id
|
||||||
|
* @param level 天气的等级
|
||||||
|
* @returns 天气实例,可以操作天气的效果,也可以用来删除
|
||||||
|
*/
|
||||||
|
activate(id: string, level?: number) {
|
||||||
|
const Weather = WeatherController.list.get(id);
|
||||||
|
if (!Weather) {
|
||||||
|
logger.warn(25, id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const weather = new Weather(level);
|
||||||
|
this.active.add(weather);
|
||||||
|
weather.activate();
|
||||||
|
if (!this.ticker.funcs.has(this.tick)) {
|
||||||
|
this.ticker.add(this.tick);
|
||||||
|
}
|
||||||
|
return weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一个天气
|
||||||
|
* @param weather 要删除的天气
|
||||||
|
*/
|
||||||
|
deactivate(weather: IWeather) {
|
||||||
|
this.active.delete(weather);
|
||||||
|
if (this.active.size === 0) {
|
||||||
|
this.ticker.remove(this.tick);
|
||||||
|
}
|
||||||
|
weather.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static register(weather: Weather) {
|
||||||
|
this.list.set(weather.id, weather);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user