mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 20:32:58 +08:00 
			
		
		
		
	feat: 雪花(半成品)#1
This commit is contained in:
		
							parent
							
								
									3a54ed6535
								
							
						
					
					
						commit
						891763387a
					
				| @ -1,3 +1,5 @@ | ||||
| import { WeatherController } from './../module/weather/weather'; | ||||
| import '@/module/weather/snow'; | ||||
| import { BgmController, bgm } from './audio/bgm'; | ||||
| import { SoundController, SoundEffect, sound } from './audio/sound'; | ||||
| import { Focus, GameUi, UiController } from './main/custom/ui'; | ||||
| @ -174,3 +176,8 @@ Mota.register('module', 'Animation', Animation); | ||||
| 
 | ||||
| main.renderLoaded = true; | ||||
| Mota.require('var', 'hook').emit('renderLoaded'); | ||||
| 
 | ||||
| const weather = new WeatherController(); | ||||
| Mota.require('var', 'hook').once('reset', () => { | ||||
|     weather.activate('snow'); | ||||
| }); | ||||
|  | ||||
							
								
								
									
										261
									
								
								src/module/weather/snow.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								src/module/weather/snow.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | ||||
| import { | ||||
|     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 snowVs = /* glsl */ ` | ||||
| in vec2 a_snowVertex; | ||||
| 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; | ||||
|      | ||||
|     // const int segments = 32; // 圆的分段数
 | ||||
|     // float angleIncrement = 2.0 * 3.14159265359 / float(segments);
 | ||||
|     // vec2 a_roundVertex[segments];
 | ||||
|     // for (int i = 0; i < segments; i++) {
 | ||||
|     //     float angle = float(i) * angleIncrement;
 | ||||
|     //     a_roundVertex[i] = vec2(cos(angle), sin(angle)); // 生成单位圆的顶点
 | ||||
|     // }
 | ||||
| 
 | ||||
|     // vec2 pos = rotate * scale * a_roundVertex + off;
 | ||||
|     vec2 pos = rotate * scale * a_snowVertex + off; | ||||
|     v_center = off; | ||||
|     v_pos = pos; | ||||
|     gl_Position = vec4(pos, 0.0, 1.0); | ||||
| } | ||||
| `;
 | ||||
| 
 | ||||
| const snowFs = /* 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 distanceFromCenter = length(v_pos - v_center); | ||||
|     if (distanceFromCenter < 0.05) { | ||||
|         outColor = u_color; // 雪花颜色
 | ||||
|     } else { | ||||
|         // outColor = vec4(0.0, 0.0, 0.0, 0.2);
 | ||||
|         discard; // 不绘制超出圆的部分
 | ||||
|     } | ||||
| 
 | ||||
|     // 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);
 | ||||
|     // outColor = u_color;
 | ||||
| } | ||||
| `;
 | ||||
| 
 | ||||
| /** 雨滴顶点坐标 */ | ||||
| const vertex = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]); | ||||
| 
 | ||||
| Mota.require('var', 'loading').once('coreInit', () => { | ||||
|     const shader = new SnowShader(); | ||||
|     const gl = shader.gl; | ||||
|     shader.size(480, 480); | ||||
|     shader.setHD(true); | ||||
|     SnowWeather.shader = shader; | ||||
|     const program = shader.createProgram(); | ||||
|     program.setVersion(shader.VERSION_ES_300); | ||||
|     program.fs(snowFs); | ||||
|     program.vs(snowVs); | ||||
|     program.requestCompile(); | ||||
|     program.useDefault(false); | ||||
|     const pos = program.defineAttribArray('a_snowVertex'); | ||||
|     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); | ||||
|     SnowShader.snowProgram = 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); | ||||
|     SnowShader.backProgram = back; | ||||
|     shader.useProgram(back); | ||||
| }); | ||||
| 
 | ||||
| export class SnowWeather implements IWeather { | ||||
|     static id: string = 'snow'; | ||||
| 
 | ||||
|     static shader: SnowShader; | ||||
| 
 | ||||
|     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 = SnowWeather.shader; | ||||
|         layer.append(shader); | ||||
|         shader.append(draw); | ||||
| 
 | ||||
|         const gl = shader.gl; | ||||
|         const program = SnowShader.snowProgram; | ||||
|         program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, 100 * this.level); | ||||
| 
 | ||||
|         this.progress = program.getUniform<UniformType.Uniform1f>('u_progress'); | ||||
|         shader.generateSnowPath( | ||||
|             this.level * 100, | ||||
|             (((Math.random() - 0.5) * Math.PI) / 30) * this.level, | ||||
|             (Math.PI / 180) * (12 - this.level) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     frame(): void { | ||||
|         SnowWeather.shader.update(SnowWeather.shader); | ||||
|         const time = 5000 - 400 * this.level; | ||||
|         const progress = (Date.now() % time) / time; | ||||
| 
 | ||||
|         SnowWeather.shader.useProgram(SnowShader.snowProgram); | ||||
|         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 = SnowWeather.shader; | ||||
|         layer.append(draw); | ||||
|         shader.remove(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| WeatherController.register(SnowWeather); | ||||
| 
 | ||||
| class SnowShader extends Shader { | ||||
|     static snowProgram: ShaderProgram; | ||||
|     static backProgram: ShaderProgram; | ||||
| 
 | ||||
|     /** | ||||
|      * 生成雨滴 | ||||
|      * @param num 雨滴数量 | ||||
|      */ | ||||
|     generateSnowPath(num: number, angle: number, deviation: number) { | ||||
|         const program = SnowShader.snowProgram; | ||||
|         SnowWeather.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.05 + 0.03; | ||||
|             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.1 + 0.05; | ||||
|             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 = SnowShader.backProgram; | ||||
|         const snow = SnowShader.snowProgram; | ||||
|         this.useProgram(back); | ||||
|         const ready = this.defaultReady(); | ||||
|         const param = back.getDrawParams(this.DRAW_ELEMENTS); | ||||
|         const snowParam = snow.getDrawParams(this.DRAW_ARRAYS_INSTANCED); | ||||
|         if (!ready || !param || !snowParam) return false; | ||||
| 
 | ||||
|         this.draw(gl, back, param, back.usingIndices); | ||||
|         this.useProgram(snow); | ||||
|         this.draw(gl, snow, snowParam, null); | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 hunhou001
						hunhou001