@fps-games/vfx 0.3.17 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
<!-- release.mjs 在此行下方插入新版本区块 -->
|
|
14
14
|
|
|
15
|
+
## v0.3.18 — 2026-07-14
|
|
16
|
+
|
|
17
|
+
### 改动
|
|
18
|
+
- fix(tooling): support validation on Windows
|
|
19
|
+
- feat(energy-shield): align dome shader rendering
|
|
20
|
+
- feat(energy-shield): refine glass shield rendering
|
|
21
|
+
|
|
22
|
+
### 特效
|
|
23
|
+
- 无新增 / 删除(仍 41 个)。
|
|
24
|
+
|
|
15
25
|
## v0.3.16 — 2026-07-08
|
|
16
26
|
|
|
17
27
|
### 改动
|
|
@@ -10,10 +10,18 @@ export interface EnergyShieldParams extends VfxParamValues {
|
|
|
10
10
|
radius: number;
|
|
11
11
|
verticalScale: number;
|
|
12
12
|
color: VfxColorValue;
|
|
13
|
+
baseColor: VfxColorValue;
|
|
13
14
|
opacity: number;
|
|
14
15
|
fresnelPower: number;
|
|
15
16
|
fresnelStrength: number;
|
|
16
17
|
rimIntensity: number;
|
|
18
|
+
baseGlowStrength: number;
|
|
19
|
+
baseGlowHeight: number;
|
|
20
|
+
surfaceVariation: number;
|
|
21
|
+
backRimStrength: number;
|
|
22
|
+
backGlowStrength: number;
|
|
23
|
+
backGlowHeight: number;
|
|
24
|
+
showSurfaceFlow: boolean;
|
|
17
25
|
surfaceIntensity: number;
|
|
18
26
|
noiseScale: number;
|
|
19
27
|
noiseEdgeWidth: number;
|
|
@@ -42,6 +50,8 @@ export interface CreateEnergyShieldOptions {
|
|
|
42
50
|
export interface EnergyShieldHandle extends VfxEffectHandle {
|
|
43
51
|
shield: Mesh;
|
|
44
52
|
material: ShaderMaterial;
|
|
53
|
+
backShield: Mesh;
|
|
54
|
+
backMaterial: ShaderMaterial;
|
|
45
55
|
glowLayer: GlowLayer | null;
|
|
46
56
|
update(params: Partial<EnergyShieldParams>): void;
|
|
47
57
|
}
|
|
@@ -9,29 +9,36 @@ import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
|
|
|
9
9
|
import { V as VFX_RENDERING_GROUP_ID } from "../../chunks/rendering-CV3nhygX.js";
|
|
10
10
|
const SHADER_NAME = "vfxEnergyShield";
|
|
11
11
|
const HALO_ALPHA_SCALE_MAX = 0.3;
|
|
12
|
-
const
|
|
13
|
-
const HALO_FEATHER_SCALE_OFFSET = 0.018;
|
|
12
|
+
const SHIELD_SLICE = 0.5;
|
|
14
13
|
const DEFAULT_PARAMS = {
|
|
15
14
|
radius: 3.18,
|
|
16
|
-
verticalScale: 1
|
|
17
|
-
color: { r: 0.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
verticalScale: 1,
|
|
16
|
+
color: { r: 0.28627451, g: 0.50980392, b: 1 },
|
|
17
|
+
baseColor: { r: 0.28627451, g: 0.50980392, b: 1 },
|
|
18
|
+
opacity: 0.02,
|
|
19
|
+
fresnelPower: 2,
|
|
20
|
+
fresnelStrength: 1,
|
|
21
|
+
rimIntensity: 1,
|
|
22
|
+
baseGlowStrength: 0.32,
|
|
23
|
+
baseGlowHeight: 0.46,
|
|
24
|
+
surfaceVariation: 0,
|
|
25
|
+
backRimStrength: 1,
|
|
26
|
+
backGlowStrength: 0.36,
|
|
27
|
+
backGlowHeight: 0.49,
|
|
28
|
+
showSurfaceFlow: false,
|
|
29
|
+
surfaceIntensity: 0.04,
|
|
30
|
+
noiseScale: 0.9,
|
|
31
|
+
noiseEdgeWidth: 0.08,
|
|
32
|
+
noiseEdgeIntensity: 0.62,
|
|
33
|
+
noiseEdgeSmoothness: 0.3,
|
|
34
|
+
flowScale: 2.15,
|
|
35
|
+
flowSpeed: 0.18,
|
|
36
|
+
flowIntensity: 0.2,
|
|
37
|
+
marbleContrast: 1,
|
|
38
|
+
bloomIntensity: 0.4,
|
|
32
39
|
bloomKernelSize: 36,
|
|
33
|
-
showGlowLayer:
|
|
34
|
-
haloScale: 1.
|
|
40
|
+
showGlowLayer: false,
|
|
41
|
+
haloScale: 1.006,
|
|
35
42
|
showHalo: false,
|
|
36
43
|
segments: 64
|
|
37
44
|
};
|
|
@@ -61,11 +68,16 @@ precision highp float;
|
|
|
61
68
|
|
|
62
69
|
uniform float uTime;
|
|
63
70
|
uniform vec3 cameraPosition;
|
|
64
|
-
uniform vec3
|
|
71
|
+
uniform vec3 uTopColor;
|
|
72
|
+
uniform vec3 uBaseColor;
|
|
65
73
|
uniform float uOpacity;
|
|
66
74
|
uniform float uFresnelPower;
|
|
67
|
-
uniform float
|
|
75
|
+
uniform float uRimStrength;
|
|
68
76
|
uniform float uRimIntensity;
|
|
77
|
+
uniform float uHorizonStrength;
|
|
78
|
+
uniform float uHorizonHeight;
|
|
79
|
+
uniform float uSurfaceVariation;
|
|
80
|
+
uniform float uSurfaceFlowEnabled;
|
|
69
81
|
uniform float uSurfaceIntensity;
|
|
70
82
|
uniform float uNoiseScale;
|
|
71
83
|
uniform float uNoiseEdgeWidth;
|
|
@@ -75,137 +87,68 @@ uniform float uFlowScale;
|
|
|
75
87
|
uniform float uFlowSpeed;
|
|
76
88
|
uniform float uFlowIntensity;
|
|
77
89
|
uniform float uMarbleContrast;
|
|
78
|
-
uniform float
|
|
79
|
-
uniform float uHaloMode;
|
|
90
|
+
uniform float uAlphaScale;
|
|
80
91
|
|
|
81
92
|
varying vec3 vObjPos;
|
|
82
93
|
varying vec3 vWorldPos;
|
|
83
94
|
varying vec3 vNormalW;
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
float saturate(float v) { return clamp(v, 0.0, 1.0); }
|
|
91
|
-
|
|
92
|
-
float snoise(vec3 v) {
|
|
93
|
-
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
|
|
94
|
-
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
|
95
|
-
vec3 i = floor(v + dot(v, C.yyy));
|
|
96
|
-
vec3 x0 = v - i + dot(i, C.xxx);
|
|
97
|
-
vec3 g = step(x0.yzx, x0.xyz);
|
|
98
|
-
vec3 l = 1.0 - g;
|
|
99
|
-
vec3 i1 = min(g.xyz, l.zxy);
|
|
100
|
-
vec3 i2 = max(g.xyz, l.zxy);
|
|
101
|
-
vec3 x1 = x0 - i1 + C.xxx;
|
|
102
|
-
vec3 x2 = x0 - i2 + C.yyy;
|
|
103
|
-
vec3 x3 = x0 - D.yyy;
|
|
104
|
-
i = mod289v3(i);
|
|
105
|
-
vec4 p = permute(permute(permute(
|
|
106
|
-
i.z + vec4(0.0, i1.z, i2.z, 1.0))
|
|
107
|
-
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
|
|
108
|
-
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
|
|
109
|
-
float n_ = 0.142857142857;
|
|
110
|
-
vec3 ns = n_ * D.wyz - D.xzx;
|
|
111
|
-
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
|
|
112
|
-
vec4 x_ = floor(j * ns.z);
|
|
113
|
-
vec4 y_ = floor(j - 7.0 * x_);
|
|
114
|
-
vec4 x = x_ * ns.x + ns.yyyy;
|
|
115
|
-
vec4 y = y_ * ns.x + ns.yyyy;
|
|
116
|
-
vec4 h = 1.0 - abs(x) - abs(y);
|
|
117
|
-
vec4 b0 = vec4(x.xy, y.xy);
|
|
118
|
-
vec4 b1 = vec4(x.zw, y.zw);
|
|
119
|
-
vec4 s0 = floor(b0) * 2.0 + 1.0;
|
|
120
|
-
vec4 s1 = floor(b1) * 2.0 + 1.0;
|
|
121
|
-
vec4 sh = -step(h, vec4(0.0));
|
|
122
|
-
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
|
123
|
-
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
|
124
|
-
vec3 p0 = vec3(a0.xy, h.x);
|
|
125
|
-
vec3 p1 = vec3(a0.zw, h.y);
|
|
126
|
-
vec3 p2 = vec3(a1.xy, h.z);
|
|
127
|
-
vec3 p3 = vec3(a1.zw, h.w);
|
|
128
|
-
vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
|
129
|
-
p0 *= norm.x;
|
|
130
|
-
p1 *= norm.y;
|
|
131
|
-
p2 *= norm.z;
|
|
132
|
-
p3 *= norm.w;
|
|
133
|
-
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
|
134
|
-
m = m * m;
|
|
135
|
-
return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
|
96
|
+
float hash(vec3 position) {
|
|
97
|
+
position = fract(position * 0.3183099 + 0.1);
|
|
98
|
+
position *= 17.0;
|
|
99
|
+
return fract(position.x * position.y * position.z * (position.x + position.y + position.z));
|
|
136
100
|
}
|
|
137
101
|
|
|
138
|
-
float
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
102
|
+
float valueNoise(vec3 position) {
|
|
103
|
+
vec3 cell = floor(position);
|
|
104
|
+
vec3 local = fract(position);
|
|
105
|
+
local = local * local * (3.0 - 2.0 * local);
|
|
106
|
+
|
|
107
|
+
return mix(
|
|
108
|
+
mix(
|
|
109
|
+
mix(hash(cell), hash(cell + vec3(1.0, 0.0, 0.0)), local.x),
|
|
110
|
+
mix(hash(cell + vec3(0.0, 1.0, 0.0)), hash(cell + vec3(1.0, 1.0, 0.0)), local.x),
|
|
111
|
+
local.y
|
|
112
|
+
),
|
|
113
|
+
mix(
|
|
114
|
+
mix(hash(cell + vec3(0.0, 0.0, 1.0)), hash(cell + vec3(1.0, 0.0, 1.0)), local.x),
|
|
115
|
+
mix(hash(cell + vec3(0.0, 1.0, 1.0)), hash(cell + vec3(1.0, 1.0, 1.0)), local.x),
|
|
116
|
+
local.y
|
|
117
|
+
),
|
|
118
|
+
local.z
|
|
119
|
+
);
|
|
149
120
|
}
|
|
150
121
|
|
|
151
122
|
void main(void) {
|
|
152
|
-
vec3 normalW = normalize(vNormalW);
|
|
153
123
|
vec3 viewDir = normalize(cameraPosition - vWorldPos);
|
|
154
|
-
float
|
|
155
|
-
float
|
|
156
|
-
float
|
|
157
|
-
|
|
158
|
-
float t = uTime * uFlowSpeed;
|
|
159
|
-
vec3 spherePos = normalize(vObjPos + vec3(0.0001)) * max(0.05, uNoiseScale);
|
|
160
|
-
vec3 flowA = spherePos * uFlowScale + vec3(t, t * 0.62, t * 0.38);
|
|
161
|
-
vec3 flowB = spherePos * (uFlowScale * 1.91) + vec3(-t * 0.46, t * 0.82, -t * 0.27);
|
|
162
|
-
float n1 = fbm(flowA);
|
|
163
|
-
float n2 = fbm(flowB);
|
|
164
|
-
float cloud = saturate((n1 * 0.58 + n2 * 0.42) * 0.5 + 0.5);
|
|
165
|
-
|
|
166
|
-
float veinInput = spherePos.y * 0.92 + n1 * 1.65 + n2 * 0.74 + t * 0.13;
|
|
167
|
-
float ribbon = abs(sin(veinInput * 3.14159265));
|
|
168
|
-
float marble = pow(1.0 - ribbon, max(0.25, uMarbleContrast));
|
|
169
|
-
float nebula = saturate(mix(cloud, marble, 0.58) * uFlowIntensity * 0.34);
|
|
170
|
-
|
|
171
|
-
float edgeWidth = max(0.01, uNoiseEdgeWidth);
|
|
172
|
-
float edgeSoft = max(0.01, uNoiseEdgeSmoothness) * 0.22;
|
|
173
|
-
float noiseEdge = smoothstep(1.0 - edgeWidth - edgeSoft, 1.0 - edgeWidth * 0.22, nebula);
|
|
174
|
-
|
|
175
|
-
vec3 deepBlue = uColor * (0.12 + 0.42 * cloud);
|
|
176
|
-
vec3 marbleBlue = uColor * (0.34 + 0.9 * marble) * uSurfaceIntensity;
|
|
177
|
-
vec3 rimBlue = uColor * fresnel * uRimIntensity;
|
|
178
|
-
vec3 edgeBlue = uColor * noiseEdge * uNoiseEdgeIntensity;
|
|
179
|
-
vec3 color = min((deepBlue + marbleBlue + rimBlue + edgeBlue) * 0.72, vec3(1.12));
|
|
180
|
-
|
|
181
|
-
float alpha = uOpacity * (0.16 + 0.34 * cloud + 0.24 * marble);
|
|
182
|
-
alpha += fresnel * uOpacity * 0.58;
|
|
183
|
-
alpha += noiseEdge * uNoiseEdgeIntensity * 0.05;
|
|
124
|
+
float facing = abs(dot(normalize(vNormalW), viewDir));
|
|
125
|
+
float rim = pow(1.0 - facing, max(0.05, uFresnelPower));
|
|
126
|
+
float horizon = pow(1.0 - clamp(vObjPos.y / max(0.001, uHorizonHeight), 0.0, 1.0), 2.5);
|
|
127
|
+
float heightFactor = smoothstep(0.0, 0.92, vObjPos.y);
|
|
184
128
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
float featherOut = 1.0 - smoothstep(0.72, 1.0, haloRim);
|
|
189
|
-
float featherRim = featherIn * featherOut;
|
|
190
|
-
float outerFade = 1.0 - smoothstep(0.88, 1.0, haloRim);
|
|
191
|
-
color = uColor * (0.2 + featherRim * 0.24 + cloud * 0.04);
|
|
192
|
-
alpha = featherRim * 0.58 * outerFade;
|
|
193
|
-
} else if (uHaloMode > 0.5) {
|
|
194
|
-
float haloRim = pow(viewRim, max(0.3, uFresnelPower * 0.48));
|
|
195
|
-
float bodyIn = smoothstep(0.34, 0.68, haloRim);
|
|
196
|
-
float bodyOut = 1.0 - smoothstep(0.72, 1.0, haloRim);
|
|
197
|
-
float bodyRim = bodyIn * bodyOut;
|
|
198
|
-
float featherIn = smoothstep(0.12, 0.44, haloRim);
|
|
199
|
-
float featherOut = 1.0 - smoothstep(0.48, 1.0, haloRim);
|
|
200
|
-
float featherRim = featherIn * featherOut;
|
|
201
|
-
float outerFade = 1.0 - smoothstep(0.86, 1.0, haloRim);
|
|
202
|
-
color = uColor * (0.24 + bodyRim * 0.34 + featherRim * 0.12 + cloud * 0.05);
|
|
203
|
-
alpha = (bodyRim * 0.5 + featherRim * 0.14 + noiseEdge * 0.01) * outerFade;
|
|
129
|
+
vec3 motion = vec3(0.0);
|
|
130
|
+
if (uSurfaceFlowEnabled > 0.5) {
|
|
131
|
+
motion = vec3(uTime * uFlowSpeed, -uTime * uFlowSpeed * 0.62, uTime * uFlowSpeed * 0.38);
|
|
204
132
|
}
|
|
133
|
+
float cloud = valueNoise(vObjPos * max(0.05, uNoiseScale)) * 0.65;
|
|
134
|
+
cloud += valueNoise(vObjPos * max(0.05, uFlowScale) + motion) * 0.35;
|
|
135
|
+
float shapedCloud = pow(clamp(cloud, 0.0, 1.0), max(0.25, uMarbleContrast));
|
|
136
|
+
float variation = mix(1.0 - uSurfaceVariation, 1.0 + uSurfaceVariation, shapedCloud);
|
|
137
|
+
float cloudShade = mix(0.92, 1.08, shapedCloud);
|
|
138
|
+
rim *= cloudShade;
|
|
205
139
|
|
|
206
|
-
|
|
140
|
+
float edgeWidth = max(0.005, uNoiseEdgeWidth);
|
|
141
|
+
float edgeSoftness = max(0.005, uNoiseEdgeSmoothness * 0.2);
|
|
142
|
+
float flowBand = 1.0 - smoothstep(edgeWidth, edgeWidth + edgeSoftness, abs(shapedCloud - 0.5));
|
|
143
|
+
float flowGlow = flowBand * uFlowIntensity * uNoiseEdgeIntensity * uSurfaceFlowEnabled;
|
|
207
144
|
|
|
208
|
-
|
|
145
|
+
float alpha = uOpacity * variation + rim * uRimStrength + horizon * uHorizonStrength * variation;
|
|
146
|
+
alpha += flowGlow * uOpacity * 0.5;
|
|
147
|
+
vec3 gradientColor = mix(uBaseColor, uTopColor, heightFactor);
|
|
148
|
+
vec3 color = gradientColor * (0.25 + rim * 1.3 + horizon * 0.85) * cloudShade * uRimIntensity;
|
|
149
|
+
color += gradientColor * flowGlow * uSurfaceIntensity;
|
|
150
|
+
|
|
151
|
+
gl_FragColor = vec4(color, clamp(alpha * uAlphaScale, 0.0, 0.88));
|
|
209
152
|
}
|
|
210
153
|
`;
|
|
211
154
|
const energyShieldEffectPackage = {
|
|
@@ -222,11 +165,19 @@ const energyShieldEffectPackage = {
|
|
|
222
165
|
debugParams: [
|
|
223
166
|
{ key: "radius", label: "玻璃罩半径", kind: "scale", min: 0.4, max: 10, step: 0.01 },
|
|
224
167
|
{ key: "verticalScale", label: "纵向缩放", kind: "scale", min: 0.5, max: 1.8, step: 0.01 },
|
|
225
|
-
{ key: "color", label: "
|
|
226
|
-
{ key: "
|
|
227
|
-
{ key: "
|
|
228
|
-
{ key: "
|
|
168
|
+
{ key: "color", label: "顶部颜色", kind: "color" },
|
|
169
|
+
{ key: "baseColor", label: "底部颜色", kind: "color" },
|
|
170
|
+
{ key: "opacity", label: "球壳透明度", kind: "opacity", min: 0, max: 1, step: 0.01 },
|
|
171
|
+
{ key: "fresnelPower", label: "轮廓衰减", kind: "number", min: 0.2, max: 8, step: 0.01 },
|
|
172
|
+
{ key: "fresnelStrength", label: "正面轮廓强度", kind: "number", min: 0, max: 2.5, step: 0.01 },
|
|
229
173
|
{ key: "rimIntensity", label: "轮廓亮度", kind: "number", min: 0, max: 4, step: 0.01 },
|
|
174
|
+
{ key: "baseGlowStrength", label: "正面底部辉光", kind: "number", min: 0, max: 1, step: 0.01 },
|
|
175
|
+
{ key: "baseGlowHeight", label: "正面辉光高度", kind: "number", min: 0.04, max: 1, step: 0.01 },
|
|
176
|
+
{ key: "surfaceVariation", label: "表面明暗变化", kind: "number", min: 0, max: 0.6, step: 0.01 },
|
|
177
|
+
{ key: "backRimStrength", label: "背面轮廓强度", kind: "number", min: 0, max: 1.5, step: 0.01 },
|
|
178
|
+
{ key: "backGlowStrength", label: "背面底部辉光", kind: "number", min: 0, max: 1, step: 0.01 },
|
|
179
|
+
{ key: "backGlowHeight", label: "背面辉光高度", kind: "number", min: 0.04, max: 1, step: 0.01 },
|
|
180
|
+
{ key: "showSurfaceFlow", label: "显示表面流动", kind: "boolean" },
|
|
230
181
|
{ key: "surfaceIntensity", label: "表面亮度", kind: "number", min: 0, max: 3, step: 0.01 },
|
|
231
182
|
{ key: "noiseScale", label: "噪声缩放", kind: "number", min: 0.2, max: 5, step: 0.01 },
|
|
232
183
|
{ key: "noiseEdgeWidth", label: "噪声亮边宽度", kind: "number", min: 0.01, max: 0.5, step: 0.01 },
|
|
@@ -237,10 +188,10 @@ const energyShieldEffectPackage = {
|
|
|
237
188
|
{ key: "flowIntensity", label: "流动强度", kind: "number", min: 0, max: 8, step: 0.01 },
|
|
238
189
|
{ key: "marbleContrast", label: "纹路锐度", kind: "number", min: 0.25, max: 4, step: 0.01 },
|
|
239
190
|
{ key: "bloomIntensity", label: "Bloom 强度", kind: "number", min: 0, max: 4, step: 0.01 },
|
|
240
|
-
{ key: "bloomKernelSize", label: "
|
|
241
|
-
{ key: "showGlowLayer", label: "显示
|
|
242
|
-
{ key: "haloScale", label: "
|
|
243
|
-
{ key: "showHalo", label: "
|
|
191
|
+
{ key: "bloomKernelSize", label: "Bloom/Glow 半径", kind: "number", min: 8, max: 128, step: 1 },
|
|
192
|
+
{ key: "showGlowLayer", label: "显示 Bloom/Glow", kind: "boolean" },
|
|
193
|
+
{ key: "haloScale", label: "Halo 缩放", kind: "scale", min: 1, max: 1.08, step: 1e-3 },
|
|
194
|
+
{ key: "showHalo", label: "显示额外 Halo", kind: "boolean" }
|
|
244
195
|
],
|
|
245
196
|
create({ scene, root, position, renderingGroupId, params, runtime }) {
|
|
246
197
|
return createEnergyShield(scene, {
|
|
@@ -268,7 +219,7 @@ function createEnergyShield(scene, options = {}) {
|
|
|
268
219
|
{
|
|
269
220
|
diameter: 2,
|
|
270
221
|
segments: Math.max(16, Math.round(params.segments)),
|
|
271
|
-
slice:
|
|
222
|
+
slice: SHIELD_SLICE,
|
|
272
223
|
updatable: false
|
|
273
224
|
},
|
|
274
225
|
scene
|
|
@@ -278,25 +229,40 @@ function createEnergyShield(scene, options = {}) {
|
|
|
278
229
|
shield.alwaysSelectAsActiveMesh = true;
|
|
279
230
|
shield.renderingGroupId = options.renderingGroupId ?? VFX_RENDERING_GROUP_ID;
|
|
280
231
|
shield.alphaIndex = 20;
|
|
281
|
-
const material = createShieldMaterial(scene);
|
|
232
|
+
const material = createShieldMaterial(scene, { face: "front" });
|
|
282
233
|
shield.material = material;
|
|
234
|
+
const backShield = MeshBuilder.CreateSphere(
|
|
235
|
+
"vfx_energyShield_back_hemisphere",
|
|
236
|
+
{
|
|
237
|
+
diameter: 2,
|
|
238
|
+
segments: Math.max(16, Math.round(params.segments)),
|
|
239
|
+
slice: SHIELD_SLICE,
|
|
240
|
+
updatable: false
|
|
241
|
+
},
|
|
242
|
+
scene
|
|
243
|
+
);
|
|
244
|
+
backShield.parent = root;
|
|
245
|
+
backShield.isPickable = false;
|
|
246
|
+
backShield.alwaysSelectAsActiveMesh = true;
|
|
247
|
+
backShield.renderingGroupId = shield.renderingGroupId;
|
|
248
|
+
backShield.alphaIndex = shield.alphaIndex - 1;
|
|
249
|
+
const backMaterial = createShieldMaterial(scene, {
|
|
250
|
+
name: "vfx_energyShield_back_material",
|
|
251
|
+
alphaMode: Constants.ALPHA_ADD,
|
|
252
|
+
face: "back"
|
|
253
|
+
});
|
|
254
|
+
backShield.material = backMaterial;
|
|
283
255
|
const sharedGlowLayer = options.glowLayer ?? null;
|
|
284
256
|
let glowLayer = null;
|
|
285
257
|
let ownsGlowLayer = false;
|
|
286
258
|
let glowMeshIncluded = false;
|
|
287
259
|
let halo = null;
|
|
288
260
|
let haloMaterial = null;
|
|
289
|
-
let haloFeather = null;
|
|
290
|
-
let haloFeatherMaterial = null;
|
|
291
261
|
const disposeHalo = () => {
|
|
292
262
|
halo?.dispose(false, false);
|
|
293
263
|
haloMaterial?.dispose();
|
|
294
|
-
haloFeather?.dispose(false, false);
|
|
295
|
-
haloFeatherMaterial?.dispose();
|
|
296
264
|
halo = null;
|
|
297
265
|
haloMaterial = null;
|
|
298
|
-
haloFeather = null;
|
|
299
|
-
haloFeatherMaterial = null;
|
|
300
266
|
};
|
|
301
267
|
const addGlowMesh = () => {
|
|
302
268
|
if (!glowLayer || glowMeshIncluded) return;
|
|
@@ -348,7 +314,7 @@ function createEnergyShield(scene, options = {}) {
|
|
|
348
314
|
{
|
|
349
315
|
diameter: 2,
|
|
350
316
|
segments: Math.max(16, Math.round(params.segments)),
|
|
351
|
-
slice:
|
|
317
|
+
slice: SHIELD_SLICE,
|
|
352
318
|
updatable: false
|
|
353
319
|
},
|
|
354
320
|
scene
|
|
@@ -361,43 +327,21 @@ function createEnergyShield(scene, options = {}) {
|
|
|
361
327
|
halo.scaling.setAll(Math.max(1, params.haloScale));
|
|
362
328
|
haloMaterial = createShieldMaterial(scene, {
|
|
363
329
|
name: "vfx_energyShield_halo_material",
|
|
364
|
-
alphaMode: Constants.ALPHA_ADD
|
|
330
|
+
alphaMode: Constants.ALPHA_ADD,
|
|
331
|
+
face: "front"
|
|
365
332
|
});
|
|
366
333
|
halo.material = haloMaterial;
|
|
367
334
|
}
|
|
368
|
-
if (!haloFeather || !haloFeatherMaterial) {
|
|
369
|
-
haloFeather = MeshBuilder.CreateSphere(
|
|
370
|
-
"vfx_energyShield_halo_feather",
|
|
371
|
-
{
|
|
372
|
-
diameter: 2,
|
|
373
|
-
segments: Math.max(16, Math.round(params.segments)),
|
|
374
|
-
slice: 0.5,
|
|
375
|
-
updatable: false
|
|
376
|
-
},
|
|
377
|
-
scene
|
|
378
|
-
);
|
|
379
|
-
haloFeather.parent = shield;
|
|
380
|
-
haloFeather.isPickable = false;
|
|
381
|
-
haloFeather.alwaysSelectAsActiveMesh = true;
|
|
382
|
-
haloFeather.renderingGroupId = shield.renderingGroupId;
|
|
383
|
-
haloFeather.alphaIndex = shield.alphaIndex + 2;
|
|
384
|
-
haloFeather.scaling.setAll(getHaloFeatherScale(params));
|
|
385
|
-
haloFeatherMaterial = createShieldMaterial(scene, {
|
|
386
|
-
name: "vfx_energyShield_halo_feather_material",
|
|
387
|
-
alphaMode: Constants.ALPHA_ADD
|
|
388
|
-
});
|
|
389
|
-
haloFeather.material = haloFeatherMaterial;
|
|
390
|
-
}
|
|
391
335
|
};
|
|
392
336
|
ensureGlowLayer();
|
|
393
337
|
ensureHalo();
|
|
394
|
-
applyParams(material, shield,
|
|
338
|
+
applyParams(material, shield, backMaterial, backShield, halo, haloMaterial, params);
|
|
395
339
|
let elapsed = 0;
|
|
396
340
|
const observer = scene.onBeforeRenderObservable.add(() => {
|
|
397
341
|
elapsed += Math.min(scene.getEngine().getDeltaTime() / 1e3, 0.05);
|
|
398
342
|
material.setFloat("uTime", elapsed);
|
|
343
|
+
backMaterial.setFloat("uTime", elapsed);
|
|
399
344
|
haloMaterial?.setFloat("uTime", elapsed);
|
|
400
|
-
haloFeatherMaterial?.setFloat("uTime", elapsed);
|
|
401
345
|
});
|
|
402
346
|
let disposed = false;
|
|
403
347
|
const dispose = () => {
|
|
@@ -406,6 +350,8 @@ function createEnergyShield(scene, options = {}) {
|
|
|
406
350
|
scene.onBeforeRenderObservable.remove(observer);
|
|
407
351
|
disposeGlowLayer();
|
|
408
352
|
disposeHalo();
|
|
353
|
+
backShield.dispose(false, false);
|
|
354
|
+
backMaterial.dispose();
|
|
409
355
|
shield.dispose(false, false);
|
|
410
356
|
material.dispose();
|
|
411
357
|
if (disposeRoot) root.dispose();
|
|
@@ -414,6 +360,8 @@ function createEnergyShield(scene, options = {}) {
|
|
|
414
360
|
root,
|
|
415
361
|
shield,
|
|
416
362
|
material,
|
|
363
|
+
backShield,
|
|
364
|
+
backMaterial,
|
|
417
365
|
get glowLayer() {
|
|
418
366
|
return glowLayer;
|
|
419
367
|
},
|
|
@@ -421,7 +369,7 @@ function createEnergyShield(scene, options = {}) {
|
|
|
421
369
|
params = mergeParams(partial, params);
|
|
422
370
|
ensureGlowLayer();
|
|
423
371
|
ensureHalo();
|
|
424
|
-
applyParams(material, shield,
|
|
372
|
+
applyParams(material, shield, backMaterial, backShield, halo, haloMaterial, params);
|
|
425
373
|
},
|
|
426
374
|
dispose
|
|
427
375
|
};
|
|
@@ -438,11 +386,16 @@ function createShieldMaterial(scene, options = {}) {
|
|
|
438
386
|
"worldViewProjection",
|
|
439
387
|
"cameraPosition",
|
|
440
388
|
"uTime",
|
|
441
|
-
"
|
|
389
|
+
"uTopColor",
|
|
390
|
+
"uBaseColor",
|
|
442
391
|
"uOpacity",
|
|
443
392
|
"uFresnelPower",
|
|
444
|
-
"
|
|
393
|
+
"uRimStrength",
|
|
445
394
|
"uRimIntensity",
|
|
395
|
+
"uHorizonStrength",
|
|
396
|
+
"uHorizonHeight",
|
|
397
|
+
"uSurfaceVariation",
|
|
398
|
+
"uSurfaceFlowEnabled",
|
|
446
399
|
"uSurfaceIntensity",
|
|
447
400
|
"uNoiseScale",
|
|
448
401
|
"uNoiseEdgeWidth",
|
|
@@ -452,49 +405,68 @@ function createShieldMaterial(scene, options = {}) {
|
|
|
452
405
|
"uFlowSpeed",
|
|
453
406
|
"uFlowIntensity",
|
|
454
407
|
"uMarbleContrast",
|
|
455
|
-
"
|
|
456
|
-
"uHaloMode"
|
|
408
|
+
"uAlphaScale"
|
|
457
409
|
],
|
|
458
410
|
needAlphaBlending: true
|
|
459
411
|
}
|
|
460
412
|
);
|
|
461
413
|
material.alphaMode = options.alphaMode ?? Constants.ALPHA_COMBINE;
|
|
462
|
-
material.backFaceCulling =
|
|
414
|
+
material.backFaceCulling = true;
|
|
415
|
+
material.cullBackFaces = options.face !== "back";
|
|
463
416
|
material.disableDepthWrite = true;
|
|
464
417
|
material.fogEnabled = false;
|
|
465
418
|
return material;
|
|
466
419
|
}
|
|
467
|
-
function applyParams(material, shield,
|
|
420
|
+
function applyParams(material, shield, backMaterial, backShield, halo, haloMaterial, params) {
|
|
468
421
|
shield.scaling.set(params.radius, params.radius * params.verticalScale, params.radius);
|
|
469
|
-
|
|
470
|
-
|
|
422
|
+
backShield.scaling.copyFrom(shield.scaling);
|
|
423
|
+
applyMaterialParams(material, params, {
|
|
424
|
+
opacity: params.opacity,
|
|
425
|
+
rimStrength: params.fresnelStrength,
|
|
426
|
+
horizonStrength: params.baseGlowStrength,
|
|
427
|
+
horizonHeight: params.baseGlowHeight,
|
|
428
|
+
alphaScale: 1
|
|
429
|
+
});
|
|
430
|
+
applyMaterialParams(backMaterial, params, {
|
|
431
|
+
opacity: 0,
|
|
432
|
+
rimStrength: params.backRimStrength,
|
|
433
|
+
horizonStrength: params.backGlowStrength,
|
|
434
|
+
horizonHeight: params.backGlowHeight,
|
|
435
|
+
alphaScale: 1
|
|
436
|
+
});
|
|
437
|
+
const haloAlphaScale = Math.min(
|
|
438
|
+
HALO_ALPHA_SCALE_MAX,
|
|
439
|
+
Math.max(
|
|
440
|
+
params.opacity * Math.min(1, params.bloomIntensity),
|
|
441
|
+
Math.min(1, params.fresnelStrength) * 0.2
|
|
442
|
+
)
|
|
443
|
+
);
|
|
471
444
|
if (halo && haloMaterial) {
|
|
472
445
|
halo.scaling.setAll(Math.max(1, params.haloScale));
|
|
473
446
|
applyMaterialParams(
|
|
474
447
|
haloMaterial,
|
|
475
448
|
params,
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
applyMaterialParams(
|
|
484
|
-
haloFeatherMaterial,
|
|
485
|
-
params,
|
|
486
|
-
1,
|
|
487
|
-
haloAlphaScale * HALO_FEATHER_ALPHA_RATIO,
|
|
488
|
-
2
|
|
449
|
+
{
|
|
450
|
+
opacity: 0,
|
|
451
|
+
rimStrength: params.fresnelStrength,
|
|
452
|
+
horizonStrength: 0,
|
|
453
|
+
horizonHeight: params.baseGlowHeight,
|
|
454
|
+
alphaScale: haloAlphaScale
|
|
455
|
+
}
|
|
489
456
|
);
|
|
490
457
|
}
|
|
491
458
|
}
|
|
492
|
-
function applyMaterialParams(material, params,
|
|
493
|
-
material.setColor3("
|
|
494
|
-
material.
|
|
459
|
+
function applyMaterialParams(material, params, settings) {
|
|
460
|
+
material.setColor3("uTopColor", toColor3(params.color));
|
|
461
|
+
material.setColor3("uBaseColor", toColor3(params.baseColor));
|
|
462
|
+
material.setFloat("uOpacity", settings.opacity);
|
|
495
463
|
material.setFloat("uFresnelPower", params.fresnelPower);
|
|
496
|
-
material.setFloat("
|
|
464
|
+
material.setFloat("uRimStrength", settings.rimStrength);
|
|
497
465
|
material.setFloat("uRimIntensity", params.rimIntensity);
|
|
466
|
+
material.setFloat("uHorizonStrength", settings.horizonStrength);
|
|
467
|
+
material.setFloat("uHorizonHeight", settings.horizonHeight);
|
|
468
|
+
material.setFloat("uSurfaceVariation", params.surfaceVariation);
|
|
469
|
+
material.setFloat("uSurfaceFlowEnabled", params.showSurfaceFlow ? 1 : 0);
|
|
498
470
|
material.setFloat("uSurfaceIntensity", params.surfaceIntensity);
|
|
499
471
|
material.setFloat("uNoiseScale", params.noiseScale);
|
|
500
472
|
material.setFloat("uNoiseEdgeWidth", params.noiseEdgeWidth);
|
|
@@ -504,21 +476,18 @@ function applyMaterialParams(material, params, opacity, haloAlphaScale, haloMode
|
|
|
504
476
|
material.setFloat("uFlowSpeed", params.flowSpeed);
|
|
505
477
|
material.setFloat("uFlowIntensity", params.flowIntensity);
|
|
506
478
|
material.setFloat("uMarbleContrast", params.marbleContrast);
|
|
507
|
-
material.setFloat("
|
|
508
|
-
material.setFloat("uHaloMode", haloMode);
|
|
479
|
+
material.setFloat("uAlphaScale", settings.alphaScale);
|
|
509
480
|
}
|
|
510
481
|
function mergeParams(patch, base = DEFAULT_PARAMS) {
|
|
511
482
|
return {
|
|
512
483
|
...base,
|
|
513
484
|
...patch,
|
|
514
|
-
color: patch?.color ? { ...patch.color } : { ...base.color }
|
|
485
|
+
color: patch?.color ? { ...patch.color } : { ...base.color },
|
|
486
|
+
baseColor: patch?.baseColor ? { ...patch.baseColor } : { ...base.baseColor }
|
|
515
487
|
};
|
|
516
488
|
}
|
|
517
489
|
function toColor3(color) {
|
|
518
|
-
return new Color3(color.r, color.g, color.b);
|
|
519
|
-
}
|
|
520
|
-
function getHaloFeatherScale(params) {
|
|
521
|
-
return Math.max(1, params.haloScale + HALO_FEATHER_SCALE_OFFSET);
|
|
490
|
+
return new Color3(color.r, color.g, color.b).toLinearSpace();
|
|
522
491
|
}
|
|
523
492
|
function registerShaders() {
|
|
524
493
|
Effect.ShadersStore[`${SHADER_NAME}VertexShader`] = VERTEX_SHADER;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fps-games/vfx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared VFX library for Playable projects: engine-injected effect packages + runtime. Project-pure (no imports of host project code).",
|
|
6
6
|
"publishConfig": {
|
|
@@ -3,29 +3,41 @@
|
|
|
3
3
|
"effectId": "energy-shield",
|
|
4
4
|
"params": {
|
|
5
5
|
"radius": 3.18,
|
|
6
|
-
"verticalScale": 1
|
|
6
|
+
"verticalScale": 1,
|
|
7
7
|
"color": {
|
|
8
|
-
"r": 0.
|
|
9
|
-
"g": 0.
|
|
10
|
-
"b":
|
|
8
|
+
"r": 0.28627451,
|
|
9
|
+
"g": 0.50980392,
|
|
10
|
+
"b": 1
|
|
11
11
|
},
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
12
|
+
"baseColor": {
|
|
13
|
+
"r": 0.28627451,
|
|
14
|
+
"g": 0.50980392,
|
|
15
|
+
"b": 1
|
|
16
|
+
},
|
|
17
|
+
"opacity": 0.02,
|
|
18
|
+
"fresnelPower": 2,
|
|
19
|
+
"fresnelStrength": 1,
|
|
20
|
+
"rimIntensity": 1,
|
|
21
|
+
"baseGlowStrength": 0.32,
|
|
22
|
+
"baseGlowHeight": 0.46,
|
|
23
|
+
"surfaceVariation": 0,
|
|
24
|
+
"backRimStrength": 1,
|
|
25
|
+
"backGlowStrength": 0.36,
|
|
26
|
+
"backGlowHeight": 0.49,
|
|
27
|
+
"showSurfaceFlow": false,
|
|
28
|
+
"surfaceIntensity": 0.04,
|
|
29
|
+
"noiseScale": 0.9,
|
|
30
|
+
"noiseEdgeWidth": 0.08,
|
|
31
|
+
"noiseEdgeIntensity": 0.62,
|
|
32
|
+
"noiseEdgeSmoothness": 0.3,
|
|
33
|
+
"flowScale": 2.15,
|
|
34
|
+
"flowSpeed": 0.18,
|
|
35
|
+
"flowIntensity": 0.2,
|
|
36
|
+
"marbleContrast": 1,
|
|
37
|
+
"bloomIntensity": 0.4,
|
|
26
38
|
"bloomKernelSize": 36,
|
|
27
|
-
"showGlowLayer":
|
|
28
|
-
"haloScale": 1.
|
|
39
|
+
"showGlowLayer": false,
|
|
40
|
+
"haloScale": 1.006,
|
|
29
41
|
"showHalo": false,
|
|
30
42
|
"segments": 64
|
|
31
43
|
}
|