From 58d2b5eb36e05c76f7b67db6aa90be4868ba9fec Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sat, 21 Jun 2025 20:21:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=203D=20=E5=9B=BE=E5=83=8F=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../client-modules/src/render/fx/base.ts | 51 +++++ .../client-modules/src/render/fx/image3d.ts | 109 ++++++++++ .../client-modules/src/render/fx/index.ts | 2 + .../src/fx/pointShader.ts | 1 + packages/render-core/src/transform.ts | 193 +++++++++++++++++- pnpm-lock.yaml | 2 +- 7 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 packages-user/client-modules/src/render/fx/base.ts create mode 100644 packages-user/client-modules/src/render/fx/image3d.ts create mode 100644 packages-user/client-modules/src/render/fx/index.ts diff --git a/package.json b/package.json index b4e112d..80eceec 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "mutate-animate": "^1.4.2", "ogg-opus-decoder": "^1.6.14", "opus-decoder": "^0.7.7", - "vue": "^3.5.13" + "vue": "^3.5.17" }, "devDependencies": { "@babel/cli": "^7.26.4", diff --git a/packages-user/client-modules/src/render/fx/base.ts b/packages-user/client-modules/src/render/fx/base.ts new file mode 100644 index 0000000..c5bb1d0 --- /dev/null +++ b/packages-user/client-modules/src/render/fx/base.ts @@ -0,0 +1,51 @@ +import { Shader, ShaderProgram } from '@motajs/render-core'; + +export abstract class EffectBase { + /** 当前使用的程序 */ + protected readonly program: ShaderProgram; + + constructor( + public readonly shader: Shader, + public readonly options: T + ) { + const vs = this.getVertex(options); + const fs = this.getFragment(options); + const program = shader.createProgram(ShaderProgram, vs, fs); + program.requestCompile(); + + this.program = program; + } + + /** + * 获取片段着色器代码 + * @param options 配置信息 + */ + protected abstract getVertex(options: T): string; + + /** + * 获取顶点着色器代码 + * @param options 配置信息 + */ + protected abstract getFragment(options: T): string; + + /** + * 初始化着色器程序 + * @param program 着色器程序 + * @param options 配置信息 + */ + abstract initProgram(program: ShaderProgram, options: T): void; + + /** + * 更新着色器渲染 + */ + requestUpdate() { + this.shader.update(); + } + + /** + * 使用此着色器 + */ + use() { + this.shader.useProgram(this.program); + } +} diff --git a/packages-user/client-modules/src/render/fx/image3d.ts b/packages-user/client-modules/src/render/fx/image3d.ts new file mode 100644 index 0000000..b2dec33 --- /dev/null +++ b/packages-user/client-modules/src/render/fx/image3d.ts @@ -0,0 +1,109 @@ +import { + ITransformUpdatable, + ShaderProgram, + Transform3D +} from '@motajs/render-core'; +import { EffectBase } from './base'; + +export class Image3DEffect + extends EffectBase + implements ITransformUpdatable +{ + /** 图片的模型变换 */ + private model: Transform3D = new Transform3D(); + /** 视角变换 */ + private view: Transform3D = new Transform3D(); + /** 投影变换 */ + private proj: Transform3D = new Transform3D(); + + protected getVertex(): string { + return /* glsl */ ` + uniform mat4 u_imageTransform; + + void main() { + v_texCoord = a_texCoord; + gl_Position = u_imageTransform * a_position; + } + `; + } + + protected getFragment(): string { + return /* glsl */ ` + void main() { + gl_FragColor = texture2D(u_sampler, v_texCoord); + } + `; + } + + initProgram(program: ShaderProgram): void { + program.defineUniformMatrix( + 'u_imageTransform', + this.shader.U_MATRIX_4x4 + ); + const shader = this.shader; + const aspect = shader.width / shader.height; + this.proj.perspective((Math.PI * 2) / 3, aspect, 0.01, 1000); + this.model.bind(this); + this.view.bind(this); + this.proj.bind(this); + } + + updateTransform(): void { + const matrix = this.program.getMatrix('u_imageTransform'); + if (!matrix) return; + const trans = this.model.multiply(this.view).multiply(this.proj); + matrix.set(false, trans.mat); + this.requestUpdate(); + } + + /** + * 设置模型变换 + * @param model 模型变换 + */ + setModel(model: Transform3D) { + this.model.bind(); + this.model = model; + model.bind(this); + } + + /** + * 设置视角变换 + * @param model 视角变换 + */ + setView(view: Transform3D) { + this.view.bind(); + this.view = view; + view.bind(this); + } + + /** + * 设置投影变换 + * @param model 投影变换 + */ + setProj(proj: Transform3D) { + this.proj.bind(); + this.proj = proj; + proj.bind(this); + } + + /** + * 获取模型变换 + */ + getModel() { + return this.model; + } + + /** + * 获取视角变换 + */ + getView() { + return this.view; + } + + /** + * 获取投影变换 + */ + getProj() { + return this.proj; + } +} diff --git a/packages-user/client-modules/src/render/fx/index.ts b/packages-user/client-modules/src/render/fx/index.ts new file mode 100644 index 0000000..a40ef0c --- /dev/null +++ b/packages-user/client-modules/src/render/fx/index.ts @@ -0,0 +1,2 @@ +export * from './base'; +export * from './image3d'; diff --git a/packages-user/legacy-plugin-client/src/fx/pointShader.ts b/packages-user/legacy-plugin-client/src/fx/pointShader.ts index 72bdded..9b2178c 100644 --- a/packages-user/legacy-plugin-client/src/fx/pointShader.ts +++ b/packages-user/legacy-plugin-client/src/fx/pointShader.ts @@ -110,6 +110,7 @@ export class PointEffect { private toAdd: Set = new Set(); /** 每个特效的开始时间 */ private startTime: Map = new Map(); + /** * 为着色器创建程序 * @param shader 着色器渲染元素 diff --git a/packages/render-core/src/transform.ts b/packages/render-core/src/transform.ts index 2a45733..af53975 100644 --- a/packages/render-core/src/transform.ts +++ b/packages/render-core/src/transform.ts @@ -1,4 +1,4 @@ -import { mat3, ReadonlyMat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix'; +import { mat3, mat4, ReadonlyMat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix'; export interface ITransformUpdatable { updateTransform?(): void; @@ -38,6 +38,7 @@ export class Transform { this.scaleY = 1; this.rad = 0; this.modified = false; + this.bindedObject?.updateTransform?.(); } /** @@ -189,11 +190,9 @@ export class Transform { */ multiply(transform: Transform): Transform { if (this.modified) { - const res = new Transform(); - const mat = mat3.clone(this.mat); - mat3.multiply(mat, mat, transform.mat); - res.mat = mat; - return res; + const result = new Transform(); + mat3.multiply(result.mat, this.mat, transform.mat); + return result; } else { return transform.clone(); } @@ -253,7 +252,9 @@ export class Transform { } /** 单位矩阵 */ - static readonly identity = new Transform(); + static get identity() { + return new Transform(); + } } function multiplyVec3(mat: ReadonlyMat3, vec: ReadonlyVec3): vec3 { @@ -278,3 +279,181 @@ function getScaling(mat: ReadonlyMat3): vec2 { function getRotation(mat: ReadonlyMat3): number { return Math.atan2(mat[3], mat[0]); } + +export class Transform3D { + mat: mat4 = mat4.create(); + + /** 绑定的可更新元素 */ + bindedObject?: ITransformUpdatable; + + /** + * 绑定可更新对象 + * @param obj 要绑定的对象 + */ + bind(obj?: ITransformUpdatable) { + this.bindedObject = obj; + } + + /** + * 重置为单位矩阵 + */ + reset(): this { + mat4.identity(this.mat); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 应用缩放变换 + * @param x X轴缩放 + * @param y Y轴缩放 + * @param z Z轴缩放 + */ + scale(x: number, y: number, z: number): this { + mat4.scale(this.mat, this.mat, [x, y, z]); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 应用平移变换 + * @param x X轴平移 + * @param y Y轴平移 + * @param z Z轴平移 + */ + translate(x: number, y: number, z: number): this { + mat4.translate(this.mat, this.mat, [x, y, z]); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 应用旋转变换 + * @param rad 旋转角度(弧度) + * @param axis 旋转轴 + */ + rotate(rad: number, axis: vec3): this { + mat4.rotate(this.mat, this.mat, rad, axis); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 应用绕X轴旋转 + * @param rad 旋转角度(弧度) + */ + rotateX(rad: number): this { + return this.rotate(rad, [1, 0, 0]); + } + + /** + * 应用绕Y轴旋转 + * @param rad 旋转角度(弧度) + */ + rotateY(rad: number): this { + return this.rotate(rad, [0, 1, 0]); + } + + /** + * 应用绕Z轴旋转 + * @param rad 旋转角度(弧度) + */ + rotateZ(rad: number): this { + return this.rotate(rad, [0, 0, 1]); + } + + /** + * 设置视图矩阵 + * @param eye 摄像机位置 + * @param center 目标位置 + * @param up 上方向量 + */ + lookAt(eye: vec3, center: vec3, up: vec3): this { + mat4.lookAt(this.mat, eye, center, up); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 设置透视投影矩阵 + * @param fovy 垂直视野角度(弧度) + * @param aspect 宽高比 + * @param near 近平面 + * @param far 远平面 + */ + perspective(fovy: number, aspect: number, near: number, far: number): this { + mat4.perspective(this.mat, fovy, aspect, near, far); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 设置正交投影矩阵 + * @param left 左平面 + * @param right 右平面 + * @param bottom 底平面 + * @param top 顶平面 + * @param near 近平面 + * @param far 远平面 + */ + ortho( + left: number, + right: number, + bottom: number, + top: number, + near: number, + far: number + ): this { + mat4.ortho(this.mat, left, right, bottom, top, near, far); + this.bindedObject?.updateTransform?.(); + return this; + } + + /** + * 与另一个变换矩阵相乘 + * @param transform 另一个变换矩阵 + */ + multiply(transform: Transform3D): Transform3D { + const result = new Transform3D(); + mat4.multiply(result.mat, this.mat, transform.mat); + return result; + } + + /** + * 克隆当前变换矩阵 + */ + clone(): Transform3D { + const clone = new Transform3D(); + mat4.copy(clone.mat, this.mat); + return clone; + } + + /** + * 将点应用变换 + * @param point 要变换的点 + */ + transformed(point: vec3): vec3 { + const result = vec3.create(); + vec3.transformMat4(result, point, this.mat); + return result; + } + + /** + * 将点应用逆变换 + * @param point 要逆变换的点 + */ + untransformed(point: vec3): vec3 { + const inverse = mat4.create(); + mat4.invert(inverse, this.mat); + const result = vec3.create(); + vec3.transformMat4(result, point, inverse); + return result; + } + + /** + * 静态方法:单位矩阵 + */ + static get identity(): Transform3D { + return new Transform3D(); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 957d586..e5b823f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: specifier: ^0.7.7 version: 0.7.10 vue: - specifier: ^3.5.13 + specifier: ^3.5.17 version: 3.5.17(typescript@5.8.3) devDependencies: '@babel/cli':