From df8328c33b4cd436b59abdc853b53bcf978aae0f Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 17 Sep 2025 16:16:35 +0800 Subject: [PATCH] feat: fog weather & refactor: rain weather --- .../src/render/elements/cache.ts | 6 +- .../src/render/elements/hero.ts | 3 +- .../src/render/elements/misc.ts | 4 +- .../src/render/elements/props.ts | 3 +- .../client-modules/src/render/fx/base.ts | 1 + .../client-modules/src/render/ui/main.tsx | 11 +- .../src/render/weather/index.ts | 3 +- .../src/render/weather/presets/cloud.ts | 84 +----------- .../src/render/weather/presets/cloudLike.ts | 88 +++++++++++++ .../src/render/weather/presets/fog.ts | 35 +++++ .../src/render/weather/presets/index.ts | 2 + .../src/render/weather/presets/rain.ts | 123 +++++++++++------- packages/legacy-common/src/resource.ts | 12 -- packages/render-core/src/gl2.ts | 74 +++++++---- packages/render-core/src/index.ts | 1 + packages/render-core/src/shader.ts | 23 ++-- .../src/types.ts | 0 packages/render-elements/src/graphics.ts | 4 +- packages/render-elements/src/index.ts | 1 - packages/render-vue/src/props.ts | 4 +- public/project/data.js | 1 + public/project/{materials => images}/fog.png | Bin src/types/source/data.d.ts | 1 + 23 files changed, 296 insertions(+), 188 deletions(-) create mode 100644 packages-user/client-modules/src/render/weather/presets/cloudLike.ts create mode 100644 packages-user/client-modules/src/render/weather/presets/fog.ts rename packages/{render-elements => render-core}/src/types.ts (100%) rename public/project/{materials => images}/fog.png (100%) diff --git a/packages-user/client-modules/src/render/elements/cache.ts b/packages-user/client-modules/src/render/elements/cache.ts index affd827..d8ab5d7 100644 --- a/packages-user/client-modules/src/render/elements/cache.ts +++ b/packages-user/client-modules/src/render/elements/cache.ts @@ -1,6 +1,8 @@ import { logger } from '@motajs/common'; -import { MotaOffscreenCanvas2D } from '@motajs/render-core'; -import { SizedCanvasImageSource } from '@motajs/render-elements'; +import { + MotaOffscreenCanvas2D, + SizedCanvasImageSource +} from '@motajs/render-core'; // 经过测试(https://www.measurethat.net/Benchmarks/Show/30741/1/drawimage-img-vs-canvas-vs-bitmap-cropping-fix-loading) // 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image diff --git a/packages-user/client-modules/src/render/elements/hero.ts b/packages-user/client-modules/src/render/elements/hero.ts index eedc94e..28c2b4a 100644 --- a/packages-user/client-modules/src/render/elements/hero.ts +++ b/packages-user/client-modules/src/render/elements/hero.ts @@ -1,7 +1,6 @@ -import { RenderAdapter } from '@motajs/render-core'; +import { RenderAdapter, SizedCanvasImageSource } from '@motajs/render-core'; import { logger } from '@motajs/common'; import { ILayerRenderExtends, Layer, LayerMovingRenderable } from './layer'; -import { SizedCanvasImageSource } from '@motajs/render-elements'; import EventEmitter from 'eventemitter3'; import { texture } from './cache'; import { TimingFn } from 'mutate-animate'; diff --git a/packages-user/client-modules/src/render/elements/misc.ts b/packages-user/client-modules/src/render/elements/misc.ts index a713e33..2dc13e1 100644 --- a/packages-user/client-modules/src/render/elements/misc.ts +++ b/packages-user/client-modules/src/render/elements/misc.ts @@ -4,9 +4,9 @@ import { RenderItem, RenderItemPosition, MotaOffscreenCanvas2D, - Transform + Transform, + SizedCanvasImageSource } from '@motajs/render-core'; -import { SizedCanvasImageSource } from '@motajs/render-elements'; import { isNil } from 'lodash-es'; import { RenderableData, AutotileRenderable, texture } from './cache'; import { IAnimateFrame, renderEmits } from './frame'; diff --git a/packages-user/client-modules/src/render/elements/props.ts b/packages-user/client-modules/src/render/elements/props.ts index 6b83cb9..5bfe370 100644 --- a/packages-user/client-modules/src/render/elements/props.ts +++ b/packages-user/client-modules/src/render/elements/props.ts @@ -1,5 +1,5 @@ import { BaseProps, TagDefine } from '@motajs/render-vue'; -import { Transform } from '@motajs/render-core'; +import { CanvasStyle, Transform } from '@motajs/render-core'; import { ILayerGroupRenderExtends, FloorLayer, @@ -10,7 +10,6 @@ import { import { EAnimateEvent } from './animate'; import { EIconEvent, EWinskinEvent } from './misc'; import { IEnemyCollection } from '@motajs/types'; -import { CanvasStyle } from '@motajs/render-elements'; export interface AnimateProps extends BaseProps {} diff --git a/packages-user/client-modules/src/render/fx/base.ts b/packages-user/client-modules/src/render/fx/base.ts index ff6ad3f..c665982 100644 --- a/packages-user/client-modules/src/render/fx/base.ts +++ b/packages-user/client-modules/src/render/fx/base.ts @@ -22,6 +22,7 @@ export abstract class EffectBase { this.program = program; this.shader = shader; + shader.useProgram(program); this.initProgram(program, options); } diff --git a/packages-user/client-modules/src/render/ui/main.tsx b/packages-user/client-modules/src/render/ui/main.tsx index 88d7328..f146135 100644 --- a/packages-user/client-modules/src/render/ui/main.tsx +++ b/packages-user/client-modules/src/render/ui/main.tsx @@ -8,7 +8,14 @@ import { onTick } from '@motajs/render'; import { WeatherController } from '../weather'; -import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'; +import { + defineComponent, + onMounted, + onUnmounted, + reactive, + ref, + shallowRef +} from 'vue'; import { Textbox, Tip } from '../components'; import { GameUI } from '@motajs/system-ui'; import { @@ -76,7 +83,7 @@ const MainScene = defineComponent(() => { width: 480 }; - const map = ref(); + const map = shallowRef(); const hideStatus = ref(false); const locked = ref(false); const weather = new WeatherController(); diff --git a/packages-user/client-modules/src/render/weather/index.ts b/packages-user/client-modules/src/render/weather/index.ts index 2dd7416..6a15afa 100644 --- a/packages-user/client-modules/src/render/weather/index.ts +++ b/packages-user/client-modules/src/render/weather/index.ts @@ -1,10 +1,11 @@ import { WeatherController } from './controller'; -import { CloudWeather, RainWeather, SunWeather } from './presets'; +import { CloudWeather, FogWeather, RainWeather, SunWeather } from './presets'; export function createWeather() { WeatherController.register('cloud', CloudWeather); WeatherController.register('rain', RainWeather); WeatherController.register('sun', SunWeather); + WeatherController.register('fog', FogWeather); // WeatherController.register('snow', SnowWeather); } diff --git a/packages-user/client-modules/src/render/weather/presets/cloud.ts b/packages-user/client-modules/src/render/weather/presets/cloud.ts index 1aa2b98..6f7004c 100644 --- a/packages-user/client-modules/src/render/weather/presets/cloud.ts +++ b/packages-user/client-modules/src/render/weather/presets/cloud.ts @@ -1,83 +1,9 @@ -import { MotaOffscreenCanvas2D, Sprite } from '@motajs/render-core'; -import { Weather } from '../weather'; +import { CloudLike } from './cloudLike'; +import { SizedCanvasImageSource } from '@motajs/render-core'; -export class CloudWeather extends Weather { - /** 云层的不透明度 */ - private alpha: number = 0; - /** 水平速度 */ - private vx: number = 0; - /** 竖直速度 */ - private vy: number = 0; - /** 水平位置 */ - private cx: number = 0; - /** 竖直位置 */ - private cy: number = 0; - /** 云层移动的最大速度 */ - private maxSpeed: number = 1; - /** 云层图像 */ - private image: HTMLImageElement | null = null; - /** 上一次执行速度变换的时刻 */ - private lastTick = 0; - - private drawCloud(canvas: MotaOffscreenCanvas2D) { - const ctx = canvas.ctx; - if (!this.image) return; - ctx.globalAlpha = this.alpha; - const { width, height } = this.image; - for (let x = -1; x < 2; x++) { - for (let y = -1; y < 2; y++) { - const dx = x * width + this.cx; - const dy = y * height + this.cy; - if (dx > canvas.width || dy > canvas.height) continue; - if (dx + width < 0 || dy + height < 0) continue; - ctx.drawImage(this.image, dx, dy, width, height); - } - } - } - - tick(time: number): void { - if (!this.element || !this.image) return; - this.element.update(); - const dt = time - this.lastTick; - this.lastTick = time; - if (dt > 100) return; - const dvx = (Math.random() - 0.5) * this.level * 10; - const dvy = (Math.random() - 0.5) * this.level * 10; - const addx = (dvx * dt) / 1000; - const addy = (dvy * dt) / 1000; - if (Math.sign(addx) === Math.sign(this.vx)) { - const ratio = Math.sqrt( - (this.maxSpeed - Math.abs(this.vx)) / this.maxSpeed - ); - const value = Math.abs(addx) * ratio; - this.vx += value * Math.sign(addx); - } else { - this.vx += addx; - } - if (Math.sign(addy) === Math.sign(this.vy)) { - const ratio = Math.sqrt( - (this.maxSpeed - Math.abs(this.vy)) / this.maxSpeed - ); - const value = Math.abs(addy) * ratio; - this.vy += value * Math.sign(addy); - } else { - this.vy += addy; - } - this.cx += (this.vx * dt) / 1000; - this.cy += (this.vy * dt) / 1000; - this.cx %= this.image.width; - this.cy %= this.image.height; - } - - createElement(level: number): Sprite { - const element = new Sprite('static', true); - element.setRenderFn(canvas => this.drawCloud(canvas)); - this.maxSpeed = Math.sqrt(level) * 100; - this.vx = ((Math.random() - 0.5) * this.maxSpeed) / 2; - this.vy = ((Math.random() - 0.5) * this.maxSpeed) / 2; - this.alpha = Math.sqrt(level) / 10; - this.image = core.material.images.images['cloud.png']; - return element; +export class CloudWeather extends CloudLike { + getImage(): SizedCanvasImageSource | null { + return core.material.images.images['cloud.png'] ?? null; } onDestroy(): void {} diff --git a/packages-user/client-modules/src/render/weather/presets/cloudLike.ts b/packages-user/client-modules/src/render/weather/presets/cloudLike.ts new file mode 100644 index 0000000..fcde800 --- /dev/null +++ b/packages-user/client-modules/src/render/weather/presets/cloudLike.ts @@ -0,0 +1,88 @@ +import { MotaOffscreenCanvas2D, Sprite } from '@motajs/render-core'; +import { Weather } from '../weather'; +import { SizedCanvasImageSource } from '@motajs/render-core'; + +export abstract class CloudLike extends Weather { + /** 不透明度 */ + private alpha: number = 0; + /** 水平速度 */ + private vx: number = 0; + /** 竖直速度 */ + private vy: number = 0; + /** 水平位置 */ + private cx: number = 0; + /** 竖直位置 */ + private cy: number = 0; + /** 移动的最大速度 */ + private maxSpeed: number = 1; + /** 上一次执行速度变换的时刻 */ + private lastTick = 0; + /** 绘制天气使用的图片 */ + private image: SizedCanvasImageSource | null = null; + + /** + * 获取类多云天气所使用的图片 + */ + abstract getImage(): SizedCanvasImageSource | null; + + private drawImage(canvas: MotaOffscreenCanvas2D) { + const ctx = canvas.ctx; + if (!this.image) return; + ctx.globalAlpha = this.alpha; + const { width, height } = this.image; + for (let x = -1; x < 2; x++) { + for (let y = -1; y < 2; y++) { + const dx = x * width + this.cx; + const dy = y * height + this.cy; + if (dx > canvas.width || dy > canvas.height) continue; + if (dx + width < 0 || dy + height < 0) continue; + ctx.drawImage(this.image, dx, dy, width, height); + } + } + } + + tick(time: number): void { + if (!this.element || !this.image) return; + this.element.update(); + const dt = time - this.lastTick; + this.lastTick = time; + if (dt > 100) return; + const dvx = (Math.random() - 0.5) * this.level * 10; + const dvy = (Math.random() - 0.5) * this.level * 10; + const addx = (dvx * dt) / 1000; + const addy = (dvy * dt) / 1000; + if (Math.sign(addx) === Math.sign(this.vx)) { + const ratio = Math.sqrt( + (this.maxSpeed - Math.abs(this.vx)) / this.maxSpeed + ); + const value = Math.abs(addx) * ratio; + this.vx += value * Math.sign(addx); + } else { + this.vx += addx; + } + if (Math.sign(addy) === Math.sign(this.vy)) { + const ratio = Math.sqrt( + (this.maxSpeed - Math.abs(this.vy)) / this.maxSpeed + ); + const value = Math.abs(addy) * ratio; + this.vy += value * Math.sign(addy); + } else { + this.vy += addy; + } + this.cx += (this.vx * dt) / 1000; + this.cy += (this.vy * dt) / 1000; + this.cx %= this.image.width; + this.cy %= this.image.height; + } + + createElement(level: number): Sprite { + const element = new Sprite('static', true); + element.setRenderFn(canvas => this.drawImage(canvas)); + this.maxSpeed = Math.sqrt(level) * 100; + this.vx = ((Math.random() - 0.5) * this.maxSpeed) / 2; + this.vy = ((Math.random() - 0.5) * this.maxSpeed) / 2; + this.alpha = Math.sqrt(level) / 10; + this.image = this.getImage(); + return element; + } +} diff --git a/packages-user/client-modules/src/render/weather/presets/fog.ts b/packages-user/client-modules/src/render/weather/presets/fog.ts new file mode 100644 index 0000000..0ad39fb --- /dev/null +++ b/packages-user/client-modules/src/render/weather/presets/fog.ts @@ -0,0 +1,35 @@ +import { MotaOffscreenCanvas2D } from '@motajs/render-core'; +import { CloudLike } from './cloudLike'; +import { SizedCanvasImageSource } from '@motajs/render-core'; + +export class FogWeather extends CloudLike { + /** 雾天气的图像比较小,因此将四个进行合并 */ + private static mergedFog: MotaOffscreenCanvas2D | null = null; + + getImage(): SizedCanvasImageSource | null { + if (FogWeather.mergedFog) { + return FogWeather.mergedFog.canvas; + } else { + return FogWeather.mergeFog(); + } + } + + onDestroy(): void {} + + /** + * 将雾天气的图片 2x2 合并 + */ + static mergeFog() { + const image = core.material.images.images['fog.png']; + if (!image) return null; + const { width, height } = image; + this.mergedFog = new MotaOffscreenCanvas2D(); + this.mergedFog.size(width * 2, height * 2); + const ctx = this.mergedFog.ctx; + ctx.drawImage(image, 0, 0, width, width); + ctx.drawImage(image, width, 0, width, width); + ctx.drawImage(image, 0, height, width, width); + ctx.drawImage(image, width, height, width, width); + return this.mergedFog.canvas; + } +} diff --git a/packages-user/client-modules/src/render/weather/presets/index.ts b/packages-user/client-modules/src/render/weather/presets/index.ts index 90f5779..c795801 100644 --- a/packages-user/client-modules/src/render/weather/presets/index.ts +++ b/packages-user/client-modules/src/render/weather/presets/index.ts @@ -1,4 +1,6 @@ export * from './cloud'; +export * from './cloudLike'; +export * from './fog'; export * from './rain'; export * from './snow'; export * from './sun'; diff --git a/packages-user/client-modules/src/render/weather/presets/rain.ts b/packages-user/client-modules/src/render/weather/presets/rain.ts index 098c944..9357b42 100644 --- a/packages-user/client-modules/src/render/weather/presets/rain.ts +++ b/packages-user/client-modules/src/render/weather/presets/rain.ts @@ -1,8 +1,9 @@ import { - Shader, ShaderProgram, IShaderUniform, - UniformType + UniformType, + MotaOffscreenCanvas2D, + GL2 } from '@motajs/render'; import { Weather } from '../weather'; @@ -73,24 +74,61 @@ void main() { 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 = mix(u_color, tex, 0.8); } `; /** 雨滴顶点坐标 */ const vertex = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]); -export class RainWeather extends Weather { - /** 下雨流程的 uniform 变量 */ - private progress: IShaderUniform | null = null; - /** 使用的着色器程序 */ - private program: ShaderProgram | null = null; +interface RainCreateData { + /** 进度变量 */ + readonly uProgress: IShaderUniform | null; + /** 下雨着色器程序 */ + readonly program: ShaderProgram; +} + +class RainShader extends GL2 { + /** 下雨程序 */ + private rainProgram: ShaderProgram | null = null; + /** 背景程序 */ + private backProgram: ShaderProgram | null = null; + + create(level: number): RainCreateData { + const gl = this.gl; + const program = this.createProgram(ShaderProgram); + program.fs(rainFs); + program.vs(rainVs); + program.requestCompile(); + this.useProgram(program); + const pos = program.defineAttribArray('a_rainVertex'); + program.defineAttribArray('a_offset'); + program.defineAttribArray('a_data'); + program.defineUniform('u_color', this.UNIFORM_4f); + const uProgress = program.defineUniform('u_progress', this.UNIFORM_1f); + program.mode(this.DRAW_ARRAYS_INSTANCED); + + if (pos) { + pos.buffer(vertex, gl.STATIC_DRAW); + pos.pointer(2, gl.FLOAT, false, 0, 0); + pos.enable(); + } + + program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, 100 * level); + + this.generateRainPath(level, program); + this.rainProgram = program; + this.backProgram = this.createProgram(ShaderProgram); + this.backProgram.requestCompile(); + + return { uProgress, program }; + } /** * 生成雨滴 * @param num 雨滴数量 */ - generateRainPath(level: number, program: ShaderProgram, shader: Shader) { + generateRainPath(level: number, program: ShaderProgram) { const num = level * 100; const angle = (((Math.random() - 0.5) * Math.PI) / 30) * level; const deviation = (Math.PI / 180) * (12 - level); @@ -98,8 +136,8 @@ export class RainWeather extends Weather { const aOffset = program.getAttribArray('a_offset'); const aData = program.getAttribArray('a_data'); const color = program.getUniform('u_color'); - const gl = shader.gl; - if (!aOffset || !aData) return; + const gl = this.gl; + if (!aOffset || !aData || !color) return; const tan = Math.tan(angle); @@ -135,52 +173,47 @@ export class RainWeather extends Weather { aData.enable(); program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, num); - color?.set(1, 1, 1, 0.1); + color.set(1, 1, 1, 0.1); } - createElement(level: number): Shader { - const shader = new Shader(); - const gl = shader.gl; - shader.size(480, 480); + protected drawScene( + canvas: MotaOffscreenCanvas2D, + gl: WebGL2RenderingContext + ): void { + const program1 = this.backProgram; + const program2 = this.rainProgram; + if (!program1 || !program2) return; + this.useProgram(program1); + program1.texTexture('u_sampler', canvas.canvas); + this.draw(gl, program1); + this.useProgram(program2); + program2.texTexture('u_sampler', canvas.canvas); + this.draw(gl, program2); + } +} + +export class RainWeather extends Weather { + /** 下雨流程的 uniform 变量 */ + private progress: IShaderUniform | null = null; + /** 下雨着色器程序 */ + private program: ShaderProgram | null = null; + + createElement(level: number): GL2 { + const shader = new RainShader(); shader.setHD(true); - shader.setZIndex(100); - const program = shader.createProgram(ShaderProgram); - program.fs(rainFs); - program.vs(rainVs); - program.requestCompile(); - 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); - shader.useProgram(program); - - if (pos) { - pos.buffer(vertex, gl.STATIC_DRAW); - pos.pointer(2, gl.FLOAT, false, 0, 0); - pos.enable(); - } - - program.paramArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, 100 * this.level); - - this.progress = program.getUniform('u_progress'); - this.generateRainPath(level, program, shader); - + const { uProgress, program } = shader.create(level); + this.progress = uProgress; this.program = program; return shader; } tick(timestamp: number): void { - if (!this.element) return; + if (!this.element || !this.program) return; this.element.update(); const time = 5000 - 400 * this.level; const progress = (timestamp % time) / time; this.progress?.set(progress); } - onDestroy(): void { - if (!this.element || !this.program) return; - this.element.deleteProgram(this.program); - } + onDestroy(): void {} } diff --git a/packages/legacy-common/src/resource.ts b/packages/legacy-common/src/resource.ts index e5f46f2..73483da 100644 --- a/packages/legacy-common/src/resource.ts +++ b/packages/legacy-common/src/resource.ts @@ -576,14 +576,6 @@ export function loadDefaultResource() { ] = res.resource; }); }); - const weathers: (keyof Weather)[] = ['fog']; - weathers.forEach(v => { - const res = LoadTask.add('material', `material/${v}.png`); - res.once('load', res => { - // @ts-expect-error 需要赋值 - core.animateFrame.weather[v] = res.resource; - }); - }); // animates { const res = LoadTask.add( @@ -619,7 +611,6 @@ export async function loadCompressedResource() { HTMLImageElement >[]; materialImages.push('keyboard'); - const weathers: (keyof Weather)[] = ['fog']; Object.entries(list).forEach(v => { const [uri, list] = v; @@ -696,9 +687,6 @@ export async function loadCompressedResource() { HTMLImageElement > ] = image; - } else if (weathers.some(v => name === v + '.png')) { - // @ts-expect-error 需要赋值 - core.animateFrame.weather[v] = image; } } diff --git a/packages/render-core/src/gl2.ts b/packages/render-core/src/gl2.ts index 6c8725a..1292be8 100644 --- a/packages/render-core/src/gl2.ts +++ b/packages/render-core/src/gl2.ts @@ -4,6 +4,7 @@ import { MotaOffscreenCanvas2D } from './canvas2d'; import { ERenderItemEvent, RenderItem, RenderItemPosition } from './item'; import { Transform } from './transform'; import { isWebGL2Supported } from './utils'; +import { SizedCanvasImageSource } from './types'; export interface IGL2ProgramPrefix { readonly VERTEX: string; @@ -170,23 +171,34 @@ export abstract class GL2 extends RenderItem< super(type, false); this.canvas = document.createElement('canvas'); - this.gl = this.canvas.getContext('webgl2')!; + const gl = this.canvas.getContext('webgl2')!; + this.gl = gl; if (!GL2.support) { this.canvas.width = 0; this.canvas.height = 0; } else { - const num = this.gl.getParameter(this.gl.MAX_TEXTURE_IMAGE_UNITS); + const num = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); if (typeof num === 'number') { this.MAX_TEXTURE_COUNT = num; } } + gl.viewport(0, 0, this.canvas.width, this.canvas.height); this.init(); } + private init() { + const gl = this.gl; + if (!gl) return; + gl.enable(gl.DEPTH_TEST); + gl.enable(gl.BLEND); + gl.depthFunc(gl.LEQUAL); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + onResize(scale: number): void { - this.sizeGL(this.width, this.height); super.onResize(scale); + this.sizeGL(this.width, this.height); } setHD(hd: boolean): void { @@ -201,9 +213,10 @@ export abstract class GL2 extends RenderItem< private sizeGL(width: number, height: number) { const ratio = this.highResolution ? devicePixelRatio : 1; - const scale = ratio * this.cache.scale; + const scale = ratio * this.scale; this.canvas.width = width * scale; this.canvas.height = height * scale; + this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); } protected render( @@ -218,12 +231,10 @@ export abstract class GL2 extends RenderItem< // 清空画布 const gl = this.gl; - gl.viewport(0, 0, this.canvas.width, this.canvas.height); gl.clearColor(0, 0, 0, 0); gl.clearDepth(1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.program.ready(); - this.drawScene(canvas, gl, this.program, transform); + this.drawScene(canvas, gl, transform); canvas.clear(); canvas.ctx.drawImage(this.canvas, 0, 0, this.width, this.height); @@ -239,7 +250,6 @@ export abstract class GL2 extends RenderItem< protected abstract drawScene( canvas: MotaOffscreenCanvas2D, gl: WebGL2RenderingContext, - program: GL2Program, transform: Transform ): void; @@ -252,6 +262,7 @@ export abstract class GL2 extends RenderItem< const indices = program.usingIndices; const param = program.getDrawParams(program.renderMode); if (!param) return; + program.ready(); switch (program.renderMode) { case RenderMode.Arrays: { const { mode, first, count } = param as DrawArraysParam; @@ -308,7 +319,6 @@ export abstract class GL2 extends RenderItem< gl.bindTexture(gl.TEXTURE_2D, tex); gl.bindFramebuffer(gl.FRAMEBUFFER, buffer); if (clear) { - gl.viewport(0, 0, this.canvas.width, this.canvas.height); gl.clearColor(0, 0, 0, 0); gl.clearDepth(1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); @@ -404,15 +414,6 @@ export abstract class GL2 extends RenderItem< this.canvas.remove(); super.destroy(); } - - private init() { - const gl = this.gl; - if (!gl) return; - gl.enable(gl.DEPTH_TEST); - gl.enable(gl.BLEND); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - gl.depthFunc(gl.LEQUAL); - } } type _U1 = [x0: number]; @@ -727,6 +728,7 @@ class ShaderUniform implements IShaderUniform { set(...params: UniformSetFn[T]): void { // 因为ts类型推导的限制,类型肯定正确,但是推导不出,所以这里直接 as any 屏蔽掉类型推导 + this.gl.useProgram(this.program.program); const [x0, x1, x2, x3] = params as any[]; switch (this.type) { case UniformType.Uniform1f: @@ -815,6 +817,7 @@ class ShaderAttrib implements IShaderAttrib { set(...params: AttribSetFn[T]) { // 因为ts类型推导的限制,类型肯定正确,但是推导不出,所以这里直接 as any 屏蔽掉类型推导 + this.gl.useProgram(this.program.program); const [x0, x1, x2, x3] = params as any[]; switch (this.type) { case AttribType.Attrib1f: @@ -878,6 +881,7 @@ class ShaderAttribArray implements IShaderAttribArray { ): void; buffer(data: any, usage: any, srcOffset?: any, length?: any): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ARRAY_BUFFER, this.data); if (typeof srcOffset === 'number') { gl.bufferData(gl.ARRAY_BUFFER, data, usage, srcOffset, length); @@ -895,6 +899,7 @@ class ShaderAttribArray implements IShaderAttribArray { ): void; sub(dstOffset: any, data: any, offset?: any, length?: any): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ARRAY_BUFFER, this.data); if (typeof offset === 'number') { gl.bufferSubData(gl.ARRAY_BUFFER, dstOffset, data, offset, length); @@ -911,6 +916,7 @@ class ShaderAttribArray implements IShaderAttribArray { p4: GLintptr ): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ARRAY_BUFFER, this.data); gl.vertexAttribPointer(this.location, p0, p1, p2, p3, p4); } @@ -922,20 +928,24 @@ class ShaderAttribArray implements IShaderAttribArray { offset: GLintptr ): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ARRAY_BUFFER, this.data); gl.vertexAttribIPointer(this.location, size, type, stride, offset); } divisor(divisor: number): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.vertexAttribDivisor(this.location, divisor); } enable(): void { + this.gl.useProgram(this.program.program); this.gl.enableVertexAttribArray(this.location); } disable(): void { + this.gl.useProgram(this.program.program); this.gl.disableVertexAttribArray(this.location); } } @@ -956,6 +966,7 @@ class ShaderIndices implements IShaderIndices { ): void; buffer(p0: any, p1: any, p2?: any, p3?: any): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.data); if (typeof p2 === 'number') { gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, p0, p1, p2, p3); @@ -973,6 +984,7 @@ class ShaderIndices implements IShaderIndices { ): void; sub(p0: any, p1: any, p2?: any, p3?: any): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.data); if (typeof p2 === 'number') { gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, p0, p1, p2, p3); @@ -991,6 +1003,7 @@ class ShaderUniformMatrix implements IShaderUniformMatrix { ) {} set(x2: GLboolean, x3: Float32List, x4?: number, x5?: number): void { + this.gl.useProgram(this.program.program); switch (this.type) { case UniformMatrix.UMatrix2x2: this.gl.uniformMatrix2fv(this.location, x2, x3, x4, x5); @@ -1037,6 +1050,7 @@ class ShaderUniformBlock implements IShaderUniformBlock { set(srcData: ArrayBufferView, srcOffset: number, length?: number): void; set(srcData: unknown, srcOffset?: unknown, length?: unknown): void { const gl = this.gl; + gl.useProgram(this.program.program); const buffer = this.buffer; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); if (srcOffset !== void 0) { @@ -1065,6 +1079,7 @@ class ShaderTexture2D implements IShaderTexture2D { set(source: TexImageSource): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.activeTexture(gl.TEXTURE0 + this.index); gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); @@ -1096,6 +1111,7 @@ class ShaderTexture2D implements IShaderTexture2D { height: number ): void { const gl = this.gl; + gl.useProgram(this.program.program); gl.activeTexture(gl.TEXTURE0 + this.index); gl.bindTexture(gl.TEXTURE_2D, this.texture); @@ -1191,8 +1207,6 @@ export class GL2Program extends EventEmitter { /** 当前正在使用的顶点索引数组 */ usingIndices: IShaderIndices | null = null; - /** 着色器内容是否是默认内容,可以用于优化空着色器 */ - modified: boolean = false; /** 渲染模式 */ renderMode: RenderMode = RenderMode.Elements; @@ -1376,7 +1390,6 @@ export class GL2Program extends EventEmitter { vs(vs: string) { this.vertex = this.prefix.VERTEX + vs; this.shaderDirty = true; - this.modified = true; } /** @@ -1386,7 +1399,6 @@ export class GL2Program extends EventEmitter { fs(fs: string) { this.fragment = this.prefix.FRAGMENT + fs; this.shaderDirty = true; - this.modified = true; } /** @@ -1671,6 +1683,24 @@ export class GL2Program extends EventEmitter { return obj; } + /** + * 绑定纹理,自动判断应该使用 sub 还是 set + * @param program 使用的着色器程序 + * @param texture 要绑定至的纹理 + * @param source 纹理内容 + * @returns 是否绑定成功 + */ + texTexture(texture: string, source: SizedCanvasImageSource) { + const tex = this.getTexture(texture); + if (!tex) return false; + if (tex.width === source.width && tex.height === source.height) { + tex.sub(source, 0, 0, source.width, source.height); + } else { + tex.set(source); + } + return true; + } + /** * 摧毁这个着色器程序,不要直接调用,请使用 {@link GL2.deleteProgram} 来删除一个着色器程序 */ diff --git a/packages/render-core/src/index.ts b/packages/render-core/src/index.ts index 02de53c..6b2f23b 100644 --- a/packages/render-core/src/index.ts +++ b/packages/render-core/src/index.ts @@ -8,4 +8,5 @@ export * from './render'; export * from './shader'; export * from './sprite'; export * from './transform'; +export * from './types'; export * from './utils'; diff --git a/packages/render-core/src/shader.ts b/packages/render-core/src/shader.ts index 3ec2d87..cb6b315 100644 --- a/packages/render-core/src/shader.ts +++ b/packages/render-core/src/shader.ts @@ -1,5 +1,6 @@ import { MotaOffscreenCanvas2D } from './canvas2d'; import { EGL2Event, GL2, GL2Program, IGL2ProgramPrefix } from './gl2'; +import { RenderItemPosition } from './item'; const SHADER_PREFIX: IGL2ProgramPrefix = { VERTEX: /* glsl */ `#version 300 es @@ -38,20 +39,17 @@ export interface EShaderEvent extends EGL2Event {} export class Shader extends GL2< EShaderEvent | E > { + constructor(type: RenderItemPosition = 'static') { + super(type); + } + protected drawScene( canvas: MotaOffscreenCanvas2D, - gl: WebGL2RenderingContext, - program: GL2Program + gl: WebGL2RenderingContext ): void { - if (!program.modified) return; - const tex = program.getTexture('u_sampler'); - if (!tex) return; - const c = canvas.canvas; - if (tex.width === c.width && tex.height === c.height) { - tex.sub(c, 0, 0, c.width, c.height); - } else { - tex.set(c); - } + const program = this.program; + if (!program) return; + program.texTexture('u_sampler', canvas.canvas); this.draw(gl, program); } } @@ -63,9 +61,6 @@ export class ShaderProgram extends GL2Program { super(gl2, vs, fs); if (!vs) this.vs(DEFAULT_VS); if (!fs) this.fs(DEFAULT_FS); - if (!vs && !fs) { - this.modified = false; - } } ready(): boolean { diff --git a/packages/render-elements/src/types.ts b/packages/render-core/src/types.ts similarity index 100% rename from packages/render-elements/src/types.ts rename to packages/render-core/src/types.ts diff --git a/packages/render-elements/src/graphics.ts b/packages/render-elements/src/graphics.ts index 20595a2..a3c7405 100644 --- a/packages/render-elements/src/graphics.ts +++ b/packages/render-elements/src/graphics.ts @@ -2,11 +2,11 @@ import { Transform, ERenderItemEvent, RenderItem, - MotaOffscreenCanvas2D + MotaOffscreenCanvas2D, + CanvasStyle } from '@motajs/render-core'; import { logger } from '@motajs/common'; import { clamp, isEqual, isNil } from 'lodash-es'; -import { CanvasStyle } from './types'; export type CircleParams = [ cx?: number, diff --git a/packages/render-elements/src/index.ts b/packages/render-elements/src/index.ts index 17db1e8..4bdb3b2 100644 --- a/packages/render-elements/src/index.ts +++ b/packages/render-elements/src/index.ts @@ -1,3 +1,2 @@ export * from './graphics'; export * from './misc'; -export * from './types'; diff --git a/packages/render-vue/src/props.ts b/packages/render-vue/src/props.ts index 27c360c..3a2e39d 100644 --- a/packages/render-vue/src/props.ts +++ b/packages/render-vue/src/props.ts @@ -7,11 +7,11 @@ import { ElementLocator, ElementScale, CustomContainerRenderFn, - CustomContainerPropagateFn + CustomContainerPropagateFn, + CanvasStyle } from '@motajs/render-core'; import { BezierParams, - CanvasStyle, CircleParams, EllipseParams, ILineProperty, diff --git a/public/project/data.js b/public/project/data.js index 97307d1..cdcac4d 100644 --- a/public/project/data.js +++ b/public/project/data.js @@ -130,6 +130,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = "cloud.png", "def.png", "exp.png", + "fog.png", "hero1.png", "hero2.png", "hp.png", diff --git a/public/project/materials/fog.png b/public/project/images/fog.png similarity index 100% rename from public/project/materials/fog.png rename to public/project/images/fog.png diff --git a/src/types/source/data.d.ts b/src/types/source/data.d.ts index ff4acae..a5bbee0 100644 --- a/src/types/source/data.d.ts +++ b/src/types/source/data.d.ts @@ -118,6 +118,7 @@ type ImageIds = | 'cloud.png' | 'def.png' | 'exp.png' + | 'fog.png' | 'hero1.png' | 'hero2.png' | 'hp.png'