mirror of
https://github.com/galacean/engine.git
synced 2026-06-02 08:40:12 +08:00
Optimize TrailRenderer texture scale and remove widthMultiplier (#2889)
* feat: update trail texture scaling to support separate X and Y scales
This commit is contained in:
@@ -211,12 +211,12 @@ WebGLEngine.create({
|
||||
material.emissiveColor.copyFrom(config.emissive);
|
||||
trail.setMaterial(material);
|
||||
trail.time = config.time;
|
||||
trail.width = config.width;
|
||||
trail.minVertexDistance = 0.15;
|
||||
trailMaterials.push(material);
|
||||
|
||||
// Tapered width curve
|
||||
trail.widthCurve = new ParticleCurve(new CurveKey(0, 1), new CurveKey(0.8, 0.3), new CurveKey(1, 0));
|
||||
// Tapered width curve (width baked into curve values)
|
||||
const w = config.width;
|
||||
trail.widthCurve = new ParticleCurve(new CurveKey(0, w), new CurveKey(0.8, 0.3 * w), new CurveKey(1, 0));
|
||||
|
||||
// Color gradient
|
||||
const gradient = new ParticleGradient(
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b51b7d04cf5e0c04fe6f5bb333b96a1dc0b4fa903cb74730ccf10fe76e4c1804
|
||||
size 61042
|
||||
oid sha256:3e1e20c0df21d8aacf8b23d89f3fc61c2c6f13b025397f07ddfdfad6c3571e1c
|
||||
size 61033
|
||||
|
||||
@@ -2,7 +2,7 @@ attribute vec4 a_PositionBirthTime; // xyz: World position, w: Birth time (used
|
||||
attribute vec4 a_CornerTangent; // x: Corner (-1 or 1), yzw: Tangent direction
|
||||
attribute float a_Distance; // Absolute cumulative distance (written once per point)
|
||||
|
||||
uniform vec4 renderer_TrailParams; // x: Width, y: TextureMode (0: Stretch, 1: Tile), z: TextureScale
|
||||
uniform vec4 renderer_TrailParams; // x: TextureMode (0: Stretch, 1: Tile), y: TextureScaleX, z: TextureScaleY
|
||||
uniform vec2 renderer_DistanceParams; // x: HeadDistance, y: TailDistance
|
||||
uniform vec3 camera_Position;
|
||||
uniform mat4 camera_ViewMat;
|
||||
@@ -41,17 +41,15 @@ void main() {
|
||||
}
|
||||
right = right * inversesqrt(rightLenSq);
|
||||
|
||||
float widthMultiplier = evaluateParticleCurve(renderer_WidthCurve, min(relativePos, renderer_CurveMaxTime.z));
|
||||
float width = renderer_TrailParams.x * widthMultiplier;
|
||||
float width = evaluateParticleCurve(renderer_WidthCurve, min(relativePos, renderer_CurveMaxTime.z));
|
||||
vec3 worldPosition = position + right * width * 0.5 * corner;
|
||||
|
||||
gl_Position = camera_ProjMat * camera_ViewMat * vec4(worldPosition, 1.0);
|
||||
|
||||
// UV: u=corner side, v=position along trail
|
||||
float u = corner * 0.5 + 0.5;
|
||||
// Stretch: normalize to 0-1, Tile: use world distance directly
|
||||
float v = renderer_TrailParams.y == 0.0 ? relativePos : distFromHead;
|
||||
v_uv = vec2(u, v * renderer_TrailParams.z);
|
||||
// u = position along trail (affected by textureMode), v = corner side.
|
||||
float u = renderer_TrailParams.x == 0.0 ? relativePos : distFromHead;
|
||||
float v = corner * 0.5 + 0.5;
|
||||
v_uv = vec2(u * renderer_TrailParams.y, v * renderer_TrailParams.z);
|
||||
|
||||
v_color = evaluateParticleGradient(renderer_ColorKeys, renderer_CurveMaxTime.x, renderer_AlphaKeys, renderer_CurveMaxTime.y, relativePos);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,9 @@ export class TrailRenderer extends Renderer {
|
||||
|
||||
// Shader parameters
|
||||
@deepClone
|
||||
private _trailParams = new Vector4(1.0, TrailTextureMode.Stretch, 1.0, 0); // x: width, y: textureMode, z: textureScale
|
||||
private _trailParams = new Vector4(TrailTextureMode.Stretch, 1.0, 1.0, 0); // x: textureMode, y: textureScaleX, z: textureScaleY
|
||||
@deepClone
|
||||
private _textureScale = new Vector2(1.0, 1.0);
|
||||
@ignoreClone
|
||||
private _distanceParams = new Vector2(); // x: headDistance, y: tailDistance
|
||||
@ignoreClone
|
||||
@@ -120,36 +122,26 @@ export class TrailRenderer extends Renderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the trail.
|
||||
* The texture mapping mode for the trail.
|
||||
*/
|
||||
get width(): number {
|
||||
get textureMode(): TrailTextureMode {
|
||||
return this._trailParams.x;
|
||||
}
|
||||
|
||||
set width(value: number) {
|
||||
set textureMode(value: TrailTextureMode) {
|
||||
this._trailParams.x = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The texture mapping mode for the trail.
|
||||
* Scale of the UV coordinates.
|
||||
* x scales the coordinate along the trail, y scales the coordinate across the trail.
|
||||
*/
|
||||
get textureMode(): TrailTextureMode {
|
||||
return this._trailParams.y;
|
||||
get textureScale(): Vector2 {
|
||||
return this._textureScale;
|
||||
}
|
||||
|
||||
set textureMode(value: TrailTextureMode) {
|
||||
this._trailParams.y = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The texture scale when using Tile texture mode.
|
||||
*/
|
||||
get textureScale(): number {
|
||||
return this._trailParams.z;
|
||||
}
|
||||
|
||||
set textureScale(value: number) {
|
||||
this._trailParams.z = value;
|
||||
set textureScale(value: Vector2) {
|
||||
value !== this._textureScale && this._textureScale.copyFrom(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,6 +149,9 @@ export class TrailRenderer extends Renderer {
|
||||
*/
|
||||
constructor(entity: Entity) {
|
||||
super(entity);
|
||||
// @ts-ignore
|
||||
this._textureScale._onValueChanged = this._onTextureScaleChanged.bind(this);
|
||||
this._onTextureScaleChanged();
|
||||
this._initGeometry();
|
||||
}
|
||||
|
||||
@@ -299,16 +294,16 @@ export class TrailRenderer extends Renderer {
|
||||
|
||||
// Only expand by half width when there's actual/upcoming trail geometry
|
||||
if (hasTrailGeometry) {
|
||||
// Find max width multiplier from widthCurve
|
||||
let maxWidthMultiplier = 0;
|
||||
// Find max width from widthCurve
|
||||
let maxWidth = 0;
|
||||
const widthKeys = this.widthCurve.keys;
|
||||
for (let i = 0, n = widthKeys.length; i < n; i++) {
|
||||
const value = widthKeys[i].value;
|
||||
if (value > maxWidthMultiplier) {
|
||||
maxWidthMultiplier = value;
|
||||
if (value > maxWidth) {
|
||||
maxWidth = value;
|
||||
}
|
||||
}
|
||||
const halfWidth = this.width * maxWidthMultiplier * 0.5;
|
||||
const halfWidth = maxWidth * 0.5;
|
||||
min.set(min.x - halfWidth, min.y - halfWidth, min.z - halfWidth);
|
||||
max.set(max.x + halfWidth, max.y + halfWidth, max.z + halfWidth);
|
||||
}
|
||||
@@ -606,4 +601,10 @@ export class TrailRenderer extends Renderer {
|
||||
subRenderElement.set(this, material, this._primitive, subPrimitive);
|
||||
renderElement.addSubRenderElement(subRenderElement);
|
||||
}
|
||||
|
||||
@ignoreClone
|
||||
private _onTextureScaleChanged(): void {
|
||||
this._trailParams.y = this._textureScale.x;
|
||||
this._trailParams.z = this._textureScale.y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
BlendMode,
|
||||
Camera
|
||||
} from "@galacean/engine-core";
|
||||
import { Color, Vector3 } from "@galacean/engine-math";
|
||||
import { Color, Vector2, Vector3 } from "@galacean/engine-math";
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
|
||||
describe("Trail", async () => {
|
||||
@@ -38,9 +38,9 @@ describe("Trail", async () => {
|
||||
expect(trailRenderer.emitting).to.eq(true);
|
||||
expect(trailRenderer.minVertexDistance).to.eq(0.1);
|
||||
expect(trailRenderer.time).to.eq(5.0);
|
||||
expect(trailRenderer.width).to.eq(1.0);
|
||||
expect(trailRenderer.textureMode).to.eq(TrailTextureMode.Stretch);
|
||||
expect(trailRenderer.textureScale).to.eq(1.0);
|
||||
expect(trailRenderer.textureScale.x).to.eq(1.0);
|
||||
expect(trailRenderer.textureScale.y).to.eq(1.0);
|
||||
});
|
||||
|
||||
it("set emitting", () => {
|
||||
@@ -79,18 +79,6 @@ describe("Trail", async () => {
|
||||
expect(trailRenderer.time).to.eq(10.0);
|
||||
});
|
||||
|
||||
it("set width", () => {
|
||||
const rootEntity = scene.getRootEntity();
|
||||
const trailEntity = rootEntity.createChild("trail");
|
||||
const trailRenderer = trailEntity.addComponent(TrailRenderer);
|
||||
|
||||
trailRenderer.width = 0.5;
|
||||
expect(trailRenderer.width).to.eq(0.5);
|
||||
|
||||
trailRenderer.width = 2.0;
|
||||
expect(trailRenderer.width).to.eq(2.0);
|
||||
});
|
||||
|
||||
it("set textureMode", () => {
|
||||
const rootEntity = scene.getRootEntity();
|
||||
const trailEntity = rootEntity.createChild("trail");
|
||||
@@ -108,11 +96,13 @@ describe("Trail", async () => {
|
||||
const trailEntity = rootEntity.createChild("trail");
|
||||
const trailRenderer = trailEntity.addComponent(TrailRenderer);
|
||||
|
||||
trailRenderer.textureScale = 2.0;
|
||||
expect(trailRenderer.textureScale).to.eq(2.0);
|
||||
trailRenderer.textureScale = new Vector2(2.0, 0.5);
|
||||
expect(trailRenderer.textureScale.x).to.eq(2.0);
|
||||
expect(trailRenderer.textureScale.y).to.eq(0.5);
|
||||
|
||||
trailRenderer.textureScale = 0.5;
|
||||
expect(trailRenderer.textureScale).to.eq(0.5);
|
||||
trailRenderer.textureScale.set(0.5, 3.0);
|
||||
expect(trailRenderer.textureScale.x).to.eq(0.5);
|
||||
expect(trailRenderer.textureScale.y).to.eq(3.0);
|
||||
});
|
||||
|
||||
it("set widthCurve", () => {
|
||||
@@ -180,10 +170,10 @@ describe("Trail", async () => {
|
||||
const trailEntity = rootEntity.createChild("trail");
|
||||
const trailRenderer = trailEntity.addComponent(TrailRenderer);
|
||||
trailRenderer.setMaterial(new TrailMaterial(engine));
|
||||
trailRenderer.width = 2.0;
|
||||
trailRenderer.widthCurve = new ParticleCurve(new CurveKey(0, 2), new CurveKey(1, 2));
|
||||
trailRenderer.minVertexDistance = 0.1;
|
||||
|
||||
const halfWidth = trailRenderer.width * 0.5; // 1.0
|
||||
const halfWidth = 2.0 * 0.5; // 1.0
|
||||
|
||||
// Initial bounds is (0,0,0) because dirty flag is not set initially
|
||||
expect(trailRenderer.bounds.min).to.deep.include({ x: 0, y: 0, z: 0 });
|
||||
@@ -214,7 +204,7 @@ describe("Trail", async () => {
|
||||
expect(trailRenderer.bounds.max.z).to.closeTo(halfWidth, 0.01);
|
||||
|
||||
// Test width change affects bounds
|
||||
trailRenderer.width = 4.0;
|
||||
trailRenderer.widthCurve = new ParticleCurve(new CurveKey(0, 4), new CurveKey(1, 4));
|
||||
const newHalfWidth = 2.0;
|
||||
trailEntity.transform.position = new Vector3(5, 4, 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user