feat: 3D 图像效果

This commit is contained in:
unanmed 2025-06-21 20:21:47 +08:00
parent 7cd4469f2f
commit 58d2b5eb36
7 changed files with 351 additions and 9 deletions

View File

@ -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",

View File

@ -0,0 +1,51 @@
import { Shader, ShaderProgram } from '@motajs/render-core';
export abstract class EffectBase<T> {
/** 当前使用的程序 */
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);
}
}

View File

@ -0,0 +1,109 @@
import {
ITransformUpdatable,
ShaderProgram,
Transform3D
} from '@motajs/render-core';
import { EffectBase } from './base';
export class Image3DEffect
extends EffectBase<void>
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;
}
}

View File

@ -0,0 +1,2 @@
export * from './base';
export * from './image3d';

View File

@ -110,6 +110,7 @@ export class PointEffect {
private toAdd: Set<number[]> = new Set();
/** 每个特效的开始时间 */
private startTime: Map<number, number> = new Map();
/**
*
* @param shader

View File

@ -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();
}
}

View File

@ -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':