1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ge_log.h"
17 #include "ge_particle_circular_halo_shader.h"
18 #include "ge_visual_effect_impl.h"
19
20 namespace OHOS {
21 namespace Rosen {
22
23 namespace {
24 constexpr float DOWN_SAMPLE_SCALE = 4.0;
25 static constexpr char GLOW_HALO_PROG[] = R"(
26 uniform half2 iResolution;
27 uniform half2 rotationCenter; // center of the halo rings, normalized coords (0. - 10.)
28 uniform float DOWN_SAMPLE_SCALE;
29
30 vec4 fragColor;
31 const float PI = 3.14159;
32 const float PI2 = 6.28318;
33
34 const half2 RAND2_A = half2(127.1, 1.7);
35 const half2 RAND2_B = half2(269.5, 183.3);
36 const float RAND2_MUL = 4258.5453123;
37 // ****************************** Sub-functions ******************************
38 half2 Random2D(half2 st)
39 {
40 st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
41 return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
42 }
43
44 float Noise2D(half2 st)
45 {
46 half2 i = floor(st);
47 half2 f = fract(st);
48 half2 u = smoothstep(0.0, 1.0, f);
49 return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
50 dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
51 mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
52 dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
53 u.y);
54 }
55
56 // Simple 1D pseudo-random hash value
57 float Random1D(float x)
58 {
59 return fract(sin(x) * RAND2_MUL);
60 }
61
62 float Noise1D(float t)
63 {
64 float f = fract(t);
65 // Cubic Hermite Curve. Same as SmoothStep()
66 float u = smoothstep(0.0, 1.0, f);
67 return mix(Random1D(floor(t)), Random1D(floor(t) + 1.0), u);
68 }
69
70 // Create animated, noisy halo border lines
71 float CentralAmbienceHaloBorder(half2 uv, half2 polarCoords, float radius, float animationTime,
72 float rotationTimeScale, float noiseScale, float noiseDisplacement)
73 {
74 polarCoords.x = fract((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8));
75 float screenNoise = Noise2D(half2(uv + (animationTime * rotationTimeScale)) * noiseScale);
76 float angularRandomVal = Noise1D((polarCoords.x) * 10.0);
77 angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
78 radius += (screenNoise * noiseDisplacement);
79 polarCoords.y -= radius;
80 float thickness = mix(0.01, 0.035, angularRandomVal);
81 float circleBorder = smoothstep(thickness * radius, 0.0, abs(polarCoords.y));
82 circleBorder *= smoothstep(0.1, 1.0, angularRandomVal);
83 circleBorder *= screenNoise * 0.5 + 0.5;
84 return circleBorder;
85 }
86
87 // Create soft glow inside halo
88 float CentralAmbienceHaloGlow(half2 polarCoords, float animationTime, float radius, float glowRadius,
89 float glowExposure)
90 {
91 polarCoords.x = fract(((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8)));
92 float angularRandomVal = Noise1D(polarCoords.x * 10.);
93 angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
94 polarCoords.y -= radius;
95 float circleBorderGlow = smoothstep(glowRadius, 0.0, abs(polarCoords.y)) * glowExposure;
96 return circleBorderGlow;
97 }
98
99 float BlendScreen(float a, float b)
100 {
101 return a + b - a * b;
102 }
103
104 half4 BlendScreen(half4 a, half4 b)
105 {
106 return a + b - a * b;
107 }
108
109 // ****************************** Main Functions ******************************
110 vec4 main(vec2 fragCoord)
111 {
112 float globalRadius = 0.5 / DOWN_SAMPLE_SCALE;
113 float radius = globalRadius * 2.0;
114 float innerRadiusEdge = 0.5 * radius;
115 float outerRadiusEdge = 1.35 * radius;
116 float glowColor = 0.;
117 float solidColor = 0.0;
118
119 half2 uv = fragCoord.xy / iResolution.xy;
120 uv -= rotationCenter;
121 float screenRatio = iResolution.x / iResolution.y;
122 half2 centeredUVs = uv * 2.0 ;
123 centeredUVs.x *= screenRatio;
124 half2 directionVector = centeredUVs;
125 float lenDirVec = length(directionVector);
126 half2 polarCoords =
127 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDirVec);
128
129 if (lenDirVec > 0.5) {
130 fragColor = vec4(0.);
131 } else {
132 float ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius * 0.82,
133 globalRadius * 0.1, 1.0, 1.25, 0.05) * 2.5;
134 ambienceHaloBorder = clamp(ambienceHaloBorder, 0., 1.);
135 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
136 ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius * 0.95,
137 globalRadius * 0.25, 1.0, 0.25, 0.05);
138 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
139 ambienceHaloBorder = CentralAmbienceHaloBorder(directionVector, polarCoords, radius,
140 globalRadius * 0.35, 1.0, 0.25, 0.05);
141 solidColor = BlendScreen(solidColor, ambienceHaloBorder);
142 float ambienceHaloGlow = CentralAmbienceHaloGlow(polarCoords, 1.0, radius * 0.83, 0.2 * radius,
143 0.4);
144 float glow = clamp(ambienceHaloGlow, 0., 1.);
145 glowColor = BlendScreen(glowColor, glow); // Add a glow circle
146 }
147
148 fragColor = BlendScreen(half4(half3(solidColor), 0.0), half4(glowColor));
149 return fragColor;
150 }
151 )";
152
153 static constexpr char PARTICLE_HALO_PROG[] = R"(
154 uniform half2 iResolution;
155 uniform half randomNoise;
156 uniform half2 rotationCenter; // center of the halo rings, normalized coords (0. - 1.)
157 uniform half DOWN_SAMPLE_SCALE;
158
159 half4 fragColor;
160 // ****************************** Constants ******************************
161 const half WIDTH = 0.055; // Width of the halo rings
162 const half PI = 3.14159;
163 const half PI2 = 6.28318;
164
165 const half2 RAND2_A = half2(127.1, 1.7);
166 const half2 RAND2_B = half2(269.5, 183.3);
167 const half RAND2_MUL = 2358.5453123;
168
169 // ****************************** Sub-functions ******************************
170 half2 Random2D(half2 st)
171 {
172 st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
173 return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
174 }
175
176 half Noise2D(half2 st)
177 {
178 half2 i = floor(st);
179 half2 f = fract(st);
180 half2 u = smoothstep(0.0, 1.0, f);
181 return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
182 dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
183 mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
184 dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
185 u.y);
186 }
187
188 half Random1D(half x)
189 {
190 return fract(sin(x) * RAND2_MUL);
191 }
192
193 half Noise1D(half t)
194 {
195 half f = fract(t);
196 half u = smoothstep(0.0, 1.0, f);
197 return mix(Random1D(floor(t)), Random1D(floor(t) + 1.0), u);
198 }
199
200 half2 ShapePerturbation(half2 uv, half noiseVariation, half noiseScale, half noiseStrength)
201 {
202 half screenNoise = Noise2D(half2(uv.x - noiseVariation, uv.y + noiseVariation) * noiseScale);
203 return uv + screenNoise * noiseStrength;
204 }
205
206 half SimpleHaloRingShape(half2 uv, half radius, half noiseVariation, half haloThickness)
207 {
208 uv = ShapePerturbation(uv, noiseVariation, 1.0, radius * 0.6);
209 half2 polarCoords = half2((atan(uv.y, uv.x) + PI) / (2.0 * PI), length(uv));
210 polarCoords.y -= radius;
211 half angularRandomVal = Noise1D((polarCoords.x) * 10.0);
212 // ensure that the radial noise is seamless
213 angularRandomVal = mix(angularRandomVal, 0.5, smoothstep(0.9, 1.0, abs(polarCoords.x * 2.0 - 1.0)));
214 half thickness = mix(haloThickness, haloThickness * 4.0, angularRandomVal);
215 half circleBorder = smoothstep(thickness, thickness * 0.2, abs(polarCoords.y));
216 return clamp(circleBorder, 0.0, 1.0);
217 }
218 // ****************************** Main Functions ******************************
219 half4 main(vec2 fragCoord)
220 {
221 half globalRadius = 0.5 / DOWN_SAMPLE_SCALE;
222 half radius = globalRadius * 2.;
223 half innerRadiusEdge = 0.5 * radius;
224 half outerRadiusEdge = 1.35 * radius;
225 half haloColor = 0.;
226
227 half2 uv = fragCoord.xy / iResolution.xy;
228 half screenRatio = iResolution.x / iResolution.y;
229 uv -= rotationCenter;
230 half2 centeredUVs = uv * 2.0;
231 centeredUVs.x *= screenRatio;
232 half2 directionVector = centeredUVs;
233 half lenDir = length(directionVector);
234 if (lenDir > 0.5) {
235 fragColor = vec4(0.);
236 } else {
237 half2 polarCoords =
238 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDir);
239
240 if (lenDir >= innerRadiusEdge && lenDir <= outerRadiusEdge) {
241 half particleHaloColor = 0.;
242 half haloRing = SimpleHaloRingShape(directionVector, radius, randomNoise, WIDTH * radius);
243 particleHaloColor += haloRing;
244 haloColor = haloColor + particleHaloColor - haloColor * particleHaloColor;
245 }
246 fragColor = half4(haloColor);
247 }
248 return fragColor;
249 }
250 )";
251
252 static constexpr char MAIN_SHADER_PROG[] = R"(
253 uniform half2 iResolution;
254 uniform float globalRadius; // global radius controlling the overall size of halos
255 uniform half2 rotationCenter; // center of the halo rings, normalized coords (0. - 1.)
256 uniform shader particleHalo;
257 uniform shader glowHalo;
258 uniform float DOWN_SAMPLE_SCALE;
259
260 vec4 fragColor;
261 const int NUMBER_OF_SAMPLES = 11; // Number of samples used for blur effects
262 const float NUMBER_OF_SAMPLES_F = 11.;
263 const float PI = 3.14159;
264 const float PI2 = 6.28318;
265
266 // Color stops for the color bar
267 const float STOP_POS0 = 0.00;
268 const float STOP_POS1 = 0.22;
269 const float STOP_POS2 = 0.75;
270 const float STOP_POS3 = 0.90;
271 const float STOP_POS4 = 1.00;
272
273 const half3 STOP_COLOR0 = half3(255.0, 133.0, 127.0) / 255.0; // FF717F
274 const half3 STOP_COLOR1 = half3(86.0, 146.0, 255.0) / 255.0; // 5692FF
275 const half3 STOP_COLOR2 = half3(137.0, 240.0, 255.0) / 255.0; // 89F0FF
276 const half3 STOP_COLOR3 = half3(255.0, 200.0, 161.0) / 255.0; // FFC8A1
277 const half3 STOP_COLOR4 = half3(253.0, 216.0, 98.0) / 255.0; // FDD862
278
279 const half2 RAND2_A = half2(127.1, 1.7);
280 const half2 RAND2_B = half2(269.5, 183.3);
281 const float RAND2_MUL = 4258.5453123;
282
283 const float COS_ROT1 = 0.993482;
284 const float COS_ROT2 = 0.974012;
285 const float COS_ROT3 = 0.941844;
286 const float COS_ROT4 = 0.897398;
287 const float COS_ROT5 = 0.841254;
288 const float COS_ROT6 = 0.774142;
289 const float COS_ROT7 = 0.696938;
290 const float COS_ROT8 = 0.610648;
291 const float COS_ROT9 = 0.516397;
292 const float COS_ROT10 = 0.415415;
293
294 const float SIN_ROT1 = 0.113991;
295 const float SIN_ROT2 = 0.226497;
296 const float SIN_ROT3 = 0.336049;
297 const float SIN_ROT4 = 0.441221;
298 const float SIN_ROT5 = 0.540641;
299 const float SIN_ROT6 = 0.633012;
300 const float SIN_ROT7 = 0.717132;
301 const float SIN_ROT8 = 0.791902;
302 const float SIN_ROT9 = 0.856349;
303 const float SIN_ROT10 = 0.909632;
304
305 // ****************************** Sub-functions ******************************
306 half2 Random2D(half2 st)
307 {
308 st = half2(dot(st, RAND2_A), dot(st, RAND2_B));
309 return -1.0 + 2.0 * fract(sin(st) * RAND2_MUL);
310 }
311
312 float Noise2D(half2 st)
313 {
314 half2 i = floor(st);
315 half2 f = fract(st);
316 half2 u = smoothstep(0.0, 1.0, f);
317 return mix(mix(dot(Random2D(i + half2(0.0, 0.0)), f - half2(0.0, 0.0)),
318 dot(Random2D(i + half2(1.0, 0.0)), f - half2(1.0, 0.0)), u.x),
319 mix(dot(Random2D(i + half2(0.0, 1.0)), f - half2(0.0, 1.0)),
320 dot(Random2D(i + half2(1.0, 1.0)), f - half2(1.0, 1.0)), u.x),
321 u.y);
322 }
323
324 // Simple 1D pseudo-random hash value
325 float Random1(float x)
326 {
327 return fract(sin(x) * RAND2_MUL);
328 }
329
330 float Noise1D(float t)
331 {
332 float f = fract(t);
333 float u = smoothstep(0.0, 1.0, f);
334 return mix(Random1(floor(t)), Random1(floor(t) + 1.0), u);
335 }
336
337 // Create animated, noisy halo border lines
338 float CentralAmbienceHaloBorder(half2 uv, half2 polarCoords, float radius, float animationTime,
339 float rotationTimeScale, float noiseScale, float noiseDisplacement)
340 {
341 polarCoords.x = fract((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8));
342 float screenNoise = Noise2D(half2(uv + (animationTime * rotationTimeScale)) * noiseScale);
343 float angularRandomVal = Noise1D((polarCoords.x) * 10.0);
344 angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
345 radius += (screenNoise * noiseDisplacement);
346 polarCoords.y -= radius;
347 float thickness = mix(0.01, 0.035, angularRandomVal);
348 float circleBorder = smoothstep(thickness * radius, 0.0, abs(polarCoords.y));
349 circleBorder *= smoothstep(0.1, 1.0, angularRandomVal);
350 circleBorder *= screenNoise * 0.5 + 0.5;
351 return circleBorder;
352 }
353
354 // Create soft glow inside halo
355 float CentralAmbienceHaloGlow(half2 polarCoords, float animationTime, float radius, float glowRadius,
356 float glowExposure)
357 {
358 polarCoords.x = fract(((polarCoords.x - animationTime * 4.0) - (polarCoords.y * 0.8)));
359 float angularRandomVal = Noise1D(polarCoords.x * 10.);
360 angularRandomVal = mix(angularRandomVal, 0.2, smoothstep(0.5, 0.8, abs(polarCoords.x * 2.0 - 1.0)));
361 polarCoords.y -= radius;
362 float circleBorderGlow = smoothstep(glowRadius, 0.0, abs(polarCoords.y)) * glowExposure;
363 return circleBorderGlow;
364 }
365
366 half3 GetColorFromColorbar(half2 pt, float radius)
367 {
368 // Map pt.x to the range [0.0, 1.0] for color interpolation
369 float t = (pt.x + radius) / (2.0 * radius);
370 half3 colorValue =
371 step(pt.x, -radius) * STOP_COLOR0 +
372 // Return the first color when pt.x is to the left of the halo
373 step(STOP_POS0, t) * step(t, STOP_POS1) *
374 mix(STOP_COLOR0, STOP_COLOR1,
375 (t - STOP_POS0) / (STOP_POS1 - STOP_POS0)) + // 0.00 < t <= 0.22
376 step(STOP_POS1, t) * step(t, STOP_POS2) *
377 mix(STOP_COLOR1, STOP_COLOR2,
378 (t - STOP_POS1) / (STOP_POS2 - STOP_POS1)) + // 0.22 < t <= 0.75
379 step(STOP_POS2, t) * step(t, STOP_POS3) *
380 mix(STOP_COLOR2, STOP_COLOR3,
381 (t - STOP_POS2) / (STOP_POS3 - STOP_POS2)) + // 0.75 < t <= 0.90
382 step(STOP_POS3, t) * step(t, STOP_POS4) *
383 mix(STOP_COLOR3, STOP_COLOR4,
384 (t - STOP_POS3) / (STOP_POS4 - STOP_POS3)) + // 0.90 < t <= 1.00
385 step(STOP_POS4, t) * STOP_COLOR4; // t > 1.00
386 return colorValue;
387 }
388
389 float BlendScreen(float a, float b)
390 {
391 return a + b - a * b;
392 }
393
394 half2 UV2PixelParticle(half2 rotatedDir, float DOWN_SAMPLE_SCALE, float screenRatio, half radius,
395 half2 rotationCenter, half2 iRes)
396 {
397 half lenRotDir = length(rotatedDir);
398 half cosBeta = rotatedDir.x / lenRotDir;
399 half sinBeta = rotatedDir.y / lenRotDir;
400 rotatedDir = half2(cosBeta, sinBeta) * lenRotDir / radius / DOWN_SAMPLE_SCALE;
401
402 rotatedDir /= 2.0;
403 rotatedDir.x /= screenRatio;
404 rotatedDir += rotationCenter;
405 return rotatedDir * iRes;
406 }
407
408 // ****************************** Main Functions ******************************
409 vec4 main(vec2 fragCoord)
410 {
411 float radius = globalRadius * 2.0;
412 float innerRadiusEdge = 0.5 * radius;
413 float outerRadiusEdge = 1.35 * radius;
414 float haloColor = 0.;
415 half4 glowColor = half4(0.0);
416
417 half2 uv = fragCoord.xy / iResolution.xy;
418 uv -= rotationCenter;
419 float screenRatio = iResolution.x / iResolution.y;
420 half2 centeredUVs = uv * 2.0 ;
421 centeredUVs.x *= screenRatio;
422 half2 directionVector = centeredUVs;
423 float lenDirVec = length(directionVector);
424 half2 polarCoords =
425 half2((atan(directionVector.y, directionVector.x) + PI) / (2.0 * PI), lenDirVec);
426
427 float colorRotScale = radius * PI * 0.5; // Make the ring colors rotated.
428 mat2 rotColorRing =
429 mat2(cos(colorRotScale), -sin(colorRotScale), sin(colorRotScale), cos(colorRotScale));
430 half3 colorRing = GetColorFromColorbar(directionVector * rotColorRing, radius);
431
432 if (lenDirVec >= innerRadiusEdge && lenDirVec <= outerRadiusEdge) {
433 float particleHaloColor = 0.;
434
435 half2 rotatedDir = directionVector;
436 half2 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
437 rotationCenter, iResolution.xy);
438 half4 samplerColor = particleHalo.eval(samplePixel);
439 particleHaloColor += samplerColor.x;
440
441 rotatedDir = mat2(COS_ROT1, SIN_ROT1, -SIN_ROT1, COS_ROT1) * directionVector;
442 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
443 rotationCenter, iResolution.xy);
444 samplerColor = particleHalo.eval(samplePixel);
445 particleHaloColor += samplerColor.x;
446
447 rotatedDir = mat2(COS_ROT2, SIN_ROT2, -SIN_ROT2, COS_ROT2) * directionVector;
448 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
449 rotationCenter, iResolution.xy);
450 samplerColor = particleHalo.eval(samplePixel);
451 particleHaloColor += samplerColor.x;
452
453 rotatedDir = mat2(COS_ROT3, SIN_ROT3, -SIN_ROT3, COS_ROT3) * directionVector;
454 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
455 rotationCenter, iResolution.xy);
456 samplerColor = particleHalo.eval(samplePixel);
457 particleHaloColor += samplerColor.x;
458
459 rotatedDir = mat2(COS_ROT4, SIN_ROT4, -SIN_ROT4, COS_ROT4) * directionVector;
460 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
461 rotationCenter, iResolution.xy);
462 samplerColor = particleHalo.eval(samplePixel);
463 particleHaloColor += samplerColor.x;
464
465 rotatedDir = mat2(COS_ROT5, SIN_ROT5, -SIN_ROT5, COS_ROT5) * directionVector;
466 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
467 rotationCenter, iResolution.xy);
468 samplerColor = particleHalo.eval(samplePixel);
469 particleHaloColor += samplerColor.x;
470
471 rotatedDir = mat2(COS_ROT6, SIN_ROT6, -SIN_ROT6, COS_ROT6) * directionVector;
472 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
473 rotationCenter, iResolution.xy);
474 samplerColor = particleHalo.eval(samplePixel);
475 particleHaloColor += samplerColor.x;
476
477 rotatedDir = mat2(COS_ROT7, SIN_ROT7, -SIN_ROT7, COS_ROT7) * directionVector;
478 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
479 rotationCenter, iResolution.xy);
480 samplerColor = particleHalo.eval(samplePixel);
481 particleHaloColor += samplerColor.x;
482
483 rotatedDir = mat2(COS_ROT8, SIN_ROT8, -SIN_ROT8, COS_ROT8) * directionVector;
484 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
485 rotationCenter, iResolution.xy);
486 samplerColor = particleHalo.eval(samplePixel);
487 particleHaloColor += samplerColor.x;
488
489 rotatedDir = mat2(COS_ROT9, SIN_ROT9, -SIN_ROT9, COS_ROT9) * directionVector;
490 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
491 rotationCenter, iResolution.xy);
492 samplerColor = particleHalo.eval(samplePixel);
493 particleHaloColor += samplerColor.x;
494
495 rotatedDir = mat2(COS_ROT10, SIN_ROT10, -SIN_ROT10, COS_ROT10) * directionVector;
496 samplePixel = UV2PixelParticle(rotatedDir, DOWN_SAMPLE_SCALE, screenRatio, radius,
497 rotationCenter, iResolution.xy);
498 samplerColor = particleHalo.eval(samplePixel);
499 particleHaloColor += samplerColor.x;
500
501 particleHaloColor /= NUMBER_OF_SAMPLES_F;
502
503 haloColor = BlendScreen(haloColor, particleHaloColor);
504
505 half glowRadiusScale = 1.05;
506 float angle = radius * PI;
507 half2 rotatedUV = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)) * centeredUVs;
508 float lenRotUV = length(rotatedUV);
509 float cosAlpha = rotatedUV.x / lenRotUV;
510 float sinAlpha = rotatedUV.y / lenRotUV;
511 rotatedUV = half2(cosAlpha, sinAlpha) * lenRotUV / radius / DOWN_SAMPLE_SCALE / glowRadiusScale;
512 rotatedUV /= 2.0;
513 rotatedUV.x /= screenRatio;
514 rotatedUV += rotationCenter;
515
516 glowColor = glowHalo.eval(rotatedUV * iResolution.xy);
517 glowColor.rgb *= colorRing;
518 }
519 fragColor = half4(clamp(haloColor, 0.0, 1.0));
520 fragColor.xyz *= colorRing;
521 fragColor = fragColor + glowColor - fragColor * glowColor;
522 return fragColor;
523 }
524 )";
525 }
526
527 using CacheDataType = struct CacheData {
528 std::shared_ptr<Drawing::Image> cacheImg = nullptr;
529 };
530
GEParticleCircularHaloShader()531 GEParticleCircularHaloShader::GEParticleCircularHaloShader() {}
532
GEParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams & params)533 GEParticleCircularHaloShader::GEParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams& params)
534 {
535 particleCircularHaloParams_ = params;
536 }
537
MakeDrawingShader(const Drawing::Rect & rect,float progress)538 void GEParticleCircularHaloShader::MakeDrawingShader(const Drawing::Rect& rect, float progress)
539 {
540 drShader_ = MakeParticleCircularHaloShader(rect);
541 }
542
CreateParticleCircularHaloShader(Drawing::GEParticleCircularHaloShaderParams & params)543 std::shared_ptr<GEParticleCircularHaloShader> GEParticleCircularHaloShader::CreateParticleCircularHaloShader(
544 Drawing::GEParticleCircularHaloShaderParams& params)
545 {
546 std::shared_ptr<GEParticleCircularHaloShader> particleCircularHaloShader =
547 std::make_shared<GEParticleCircularHaloShader>(params);
548 return particleCircularHaloShader;
549 }
550
Preprocess(Drawing::Canvas & canvas,const Drawing::Rect & rect)551 void GEParticleCircularHaloShader::Preprocess(Drawing::Canvas& canvas, const Drawing::Rect& rect)
552 {
553 Drawing::ImageInfo particleHaloImg(rect.GetWidth(), rect.GetHeight(),
554 Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
555 particleHaloImg_ = MakeParticleHaloShader(canvas, particleHaloImg);
556
557 float currentCenterX = particleCircularHaloParams_.center_.first;
558 float currentCenterY = particleCircularHaloParams_.center_.second;
559 if (cacheAnyPtr_ == nullptr || lastCenterX_ != currentCenterX || lastCenterY_ != currentCenterY) {
560 CacheDataType cacheData;
561 Drawing::ImageInfo cacheImgInf(rect.GetWidth(), rect.GetHeight(),
562 Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
563 auto cacheImg = MakeGlowHaloShader(canvas, cacheImgInf);
564 if (cacheImg) {
565 cacheData.cacheImg = cacheImg;
566 cacheAnyPtr_ = std::make_shared<std::any>(std::make_any<CacheDataType>(cacheData));
567 }
568 lastCenterX_ = currentCenterX;
569 lastCenterY_ = currentCenterY;
570 }
571 }
572
GetGlowHaloBuilder()573 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetGlowHaloBuilder()
574 {
575 thread_local std::shared_ptr<Drawing::RuntimeEffect> preCalculatedGlowHaloEffect_ = nullptr;
576
577 if (preCalculatedGlowHaloEffect_ == nullptr) {
578 preCalculatedGlowHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(GLOW_HALO_PROG);
579 }
580 if (preCalculatedGlowHaloEffect_ == nullptr) {
581 GE_LOGE("GEParticleCircularHaloShader preCalculatedGlowHaloEffect_ is nullptr.");
582 return nullptr;
583 }
584 return std::make_shared<Drawing::RuntimeShaderBuilder>(preCalculatedGlowHaloEffect_);
585 }
586
GetParticleHaloBuilder()587 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetParticleHaloBuilder()
588 {
589 thread_local std::shared_ptr<Drawing::RuntimeEffect> particleHaloEffect_ = nullptr;
590
591 if (particleHaloEffect_ == nullptr) {
592 particleHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(PARTICLE_HALO_PROG);
593 }
594 if (particleHaloEffect_ == nullptr) {
595 GE_LOGE("GEParticleCircularHaloShader::GetParticleHaloBuilder particleHaloEffect_ is "
596 "nullptr.");
597 return nullptr;
598 }
599 return std::make_shared<Drawing::RuntimeShaderBuilder>(particleHaloEffect_);
600 }
601
GetParticleCircularHaloBuilder()602 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEParticleCircularHaloShader::GetParticleCircularHaloBuilder()
603 {
604 thread_local std::shared_ptr<Drawing::RuntimeEffect> glowHaloEffect_ = nullptr;
605
606 if (glowHaloEffect_ == nullptr) {
607 glowHaloEffect_ = Drawing::RuntimeEffect::CreateForShader(MAIN_SHADER_PROG);
608 }
609
610 if (glowHaloEffect_ == nullptr) {
611 GE_LOGE("GEParticleCircularHaloShader:: GetParticleCircularHaloBuilder ShaderEffect_ is nullptr.");
612 return nullptr;
613 }
614 return std::make_shared<Drawing::RuntimeShaderBuilder>(glowHaloEffect_);
615 }
616
MakeGlowHaloShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)617 std::shared_ptr<Drawing::Image> GEParticleCircularHaloShader::MakeGlowHaloShader(
618 Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
619 {
620 float width = imageInfo.GetWidth();
621 float height = imageInfo.GetHeight();
622 auto glowHaloBuilder_ = GetGlowHaloBuilder();
623 if (glowHaloBuilder_ == nullptr) {
624 GE_LOGE("GEParticleCircularHaloShader MakeGlowHaloShader preCalculatedBuilder_ is nullptr.");
625 return nullptr;
626 }
627 glowHaloBuilder_->SetUniform("iResolution", width, height);
628 glowHaloBuilder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
629 particleCircularHaloParams_.center_.second);
630 glowHaloBuilder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
631 auto preCalculatedGlowHaloShader = glowHaloBuilder_->MakeImage(canvas.GetGPUContext().get(),
632 nullptr, imageInfo, false);
633 if (preCalculatedGlowHaloShader == nullptr) {
634 GE_LOGE("GEParticleCircularHaloShader preCalculatedGlowHaloShader is nullptr.");
635 return nullptr;
636 }
637 return preCalculatedGlowHaloShader;
638 }
639
MakeParticleHaloShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)640 std::shared_ptr<Drawing::Image> GEParticleCircularHaloShader::MakeParticleHaloShader(
641 Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
642 {
643 float width = imageInfo.GetWidth();
644 float height = imageInfo.GetHeight();
645
646 particleHaloBuilder_ = GetParticleHaloBuilder();
647 if (particleHaloBuilder_ == nullptr) {
648 GE_LOGE("GEParticleCircularHaloShader MakeParticleHaloShader particleTextureBuilder_ is nullptr.");
649 return nullptr;
650 }
651 particleHaloBuilder_->SetUniform("iResolution", width, height);
652 particleHaloBuilder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
653 particleCircularHaloParams_.center_.second);
654 particleHaloBuilder_->SetUniform("randomNoise", particleCircularHaloParams_.noise_);
655 particleHaloBuilder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
656 auto particleHaloShader =
657 particleHaloBuilder_->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
658 if (particleHaloShader == nullptr) {
659 GE_LOGE("GEParticleCircularHaloShader MakeParticleHaloShader is nullptr.");
660 return nullptr;
661 }
662 return particleHaloShader;
663 }
664
MakeParticleCircularHaloShader(const Drawing::Rect & rect)665 std::shared_ptr<Drawing::ShaderEffect> GEParticleCircularHaloShader::MakeParticleCircularHaloShader(
666 const Drawing::Rect& rect)
667 {
668 if (particleHaloImg_ == nullptr) {
669 GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader particleHaloImg_ is nullptr.");
670 return nullptr;
671 }
672
673 if (cacheAnyPtr_ == nullptr) {
674 GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader cacheAnyPtr_ is nullptr.");
675 return nullptr;
676 }
677
678 auto width = rect.GetWidth();
679 auto height = rect.GetHeight();
680
681 Drawing::Matrix matrix;
682 auto preCalcImg_ = std::any_cast<CacheDataType>(*cacheAnyPtr_).cacheImg;
683 if (preCalcImg_ == nullptr) {
684 GE_LOGE("GEParticleCircularHaloShader MakeParticleCircularHaloShader preCalcImg_ is nullptr.");
685 return nullptr;
686 }
687 auto preCalcImgShader = Drawing::ShaderEffect::CreateImageShader(*preCalcImg_, Drawing::TileMode::CLAMP,
688 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
689
690 auto particleHaloShader = Drawing::ShaderEffect::CreateImageShader(*particleHaloImg_,
691 Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
692 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
693
694 builder_ = GetParticleCircularHaloBuilder();
695 if (builder_ == nullptr) {
696 GE_LOGE("GEParticleCircularHaloShader::MakeParticleCircularHaloShader builder_ is nullptr.");
697 return nullptr;
698 }
699 builder_->SetChild("glowHalo", preCalcImgShader);
700 builder_->SetChild("particleHalo", particleHaloShader);
701 builder_->SetUniform("iResolution", width, height);
702 builder_->SetUniform("globalRadius", particleCircularHaloParams_.radius_);
703 builder_->SetUniform("rotationCenter", particleCircularHaloParams_.center_.first,
704 particleCircularHaloParams_.center_.second);
705 builder_->SetUniform("DOWN_SAMPLE_SCALE", DOWN_SAMPLE_SCALE);
706 auto particleCircularHaloShader = builder_->MakeShader(nullptr, false);
707 if (particleCircularHaloShader == nullptr) {
708 GE_LOGE(
709 "GEParticleCircularHaloShader::MakeParticleCircularHaloShader particleCircularHaloShader is nullptr.");
710 return nullptr;
711 }
712 return particleCircularHaloShader;
713 }
714
715 } // namespace Rosen
716 } // namespace OHOS