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