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 #include "ge_aurora_noise_shader.h"
16
17 #include "ge_log.h"
18 #include "ge_visual_effect_impl.h"
19
20 namespace OHOS {
21 namespace Rosen {
22 namespace {
23 static constexpr char AURORA_GENERATOR_PROG[] = R"(
24 uniform vec2 iResolution;
25 uniform float noise;
26 const float contrast = 4.64; // contrast constant, 464.0 / 100.0
27 const float brightness = -0.17647; // brightness constant, -45.0 / 255.0
28 const float downSampleFactor = 8.0;
29 float SNoise(in vec3 v);
30 vec4 main(vec2 fragCoord)
31 {
32 vec2 tileSize = iResolution.xy / downSampleFactor;
33 vec2 localXY = mod(fragCoord, tileSize);
34 if (floor(fragCoord.x / tileSize.x) > 0.5 || floor(fragCoord.y / tileSize.y) > 0.5) {
35 return vec4(0.0);
36 }
37 vec2 uv = (localXY + 0.5) / tileSize;
38 float aspect = iResolution.x / iResolution.y;
39 vec2 p = vec2((uv.x / 0.75 - 1.0) * aspect, (uv.y * 2.0 - 1.0)); // horizontal stretch and map uv
40 float freqX = 2.0;
41 float freqY = mix(freqX, freqX * 0.5, smoothstep(1.0, 0.0, uv.y));
42 vec2 dom = vec2(p.x * freqX, p.y * freqY);
43 float n = abs(SNoise(vec3(dom, noise * 3.0)));
44 float alpha = clamp(1.0 - (n * contrast + brightness), 0.0, 1.0);
45 return vec4(alpha);
46 }
47
48 vec3 Mod289(in vec3 x)
49 {
50 return x - floor(x * (1.0 / 289.0)) * 289.0; // 289.0: prime number for permutation
51 }
52
53 vec4 Mod289(in vec4 x)
54 {
55 return x - floor(x * (1.0 / 289.0)) * 289.0; // 289.0: prime number for permutation
56 }
57
58 vec4 Permute(in vec4 x)
59 {
60 return Mod289(((x * 34.0) + 1.0) * x); // 34.0: prime number for permutation
61 }
62
63 vec4 TaylorInvSqrt(in vec4 r)
64 {
65 return 1.79284291400159 - 0.85373472095314 * r; // Taylor series approximation for 1/sqrt(x)
66 }
67
68 float SNoise(in vec3 v)
69 {
70 const vec2 c = vec2(0.16666666666667, 0.33333333333333); // Constants for noise function, 1/6 and 1/3
71 const vec4 d = vec4(0.0, 0.5, 1.0, 2.0); // Constants for noise function
72 // First corner
73 vec3 i = floor(v + dot(v, c.yyy));
74 vec3 x0 = v - i + dot(i, c.xxx);
75 // Other corners
76 vec3 g = step(x0.yzx, x0.xyz);
77 vec3 l = 1.0 - g;
78 vec3 i1 = min(g.xyz, l.zxy);
79 vec3 i2 = max(g.xyz, l.zxy);
80 vec3 x1 = x0 - i1 + c.xxx;
81 vec3 x2 = x0 - i2 + c.yyy; // 2.0 * c.x = 1/3 = c.y
82 vec3 x3 = x0 - d.yyy; // -1.0 + 3.0 * c.x = -0.5 = -d.y
83 // Permutations
84 i = Mod289(i);
85 vec4 p = Permute(Permute(Permute(
86 i.z + vec4(0.0, i1.z, i2.z, 1.0))
87 + i.y + vec4(0.0, i1.y, i2.y, 1.0))
88 + i.x + vec4(0.0, i1.x, i2.x, 1.0));
89 float n = 0.142857142857; // i.e., 1.0 / 7.0
90 vec3 ns = n * d.wyz - d.xzx;
91 vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // 49.0: mod(p, 7 * 7)
92 vec4 xs = floor(j * ns.z);
93 vec4 ys = floor(j - 7.0 * xs); // mod(j, N) // 7.0: mod(p, 7)
94 vec4 x = xs *ns.x + ns.yyyy;
95 vec4 y = ys *ns.x + ns.yyyy;
96 vec4 h = 1.0 - abs(x) - abs(y);
97 vec4 b0 = vec4(x.xy, y.xy);
98 vec4 b1 = vec4(x.zw, y.zw);
99 vec4 s0 = floor(b0) * 2.0 + 1.0; // 2.0: step size
100 vec4 s1 = floor(b1) * 2.0 + 1.0; // 2.0: step size
101 vec4 sh = -step(h, vec4(0.0));
102 vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy ;
103 vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww ;
104 vec3 p0 = vec3(a0.xy, h.x);
105 vec3 p1 = vec3(a0.zw, h.y);
106 vec3 p2 = vec3(a1.xy, h.z);
107 vec3 p3 = vec3(a1.zw, h.w);
108 // Normalise gradients
109 vec4 norm = TaylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
110 p0 *= norm.x;
111 p1 *= norm.y;
112 p2 *= norm.z;
113 p3 *= norm.w;
114 // Mix final noise value
115 vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); // 0.6: falloff start
116 m = m * m;
117 return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); // scale to [-1,1]
118 }
119 )";
120
121 static constexpr char AURORA_VERT_BLUR_PROG[] = R"(
122 uniform shader auroraNoiseTexture;
123 uniform vec2 iResolution;
124 const float downSampleFactor = 8.0;
125 const int sampleCount = 20;
126
127 vec4 SampleWholeTile(vec2 fragCoord, vec2 res)
128 {
129 vec2 tileSize = res / downSampleFactor;
130 vec2 uvInTile = fragCoord / res;
131 vec2 pixel = uvInTile * (tileSize - 1.0);
132 return auroraNoiseTexture.eval(pixel + 0.5);
133 }
134
135 vec4 main(vec2 fragCoord)
136 {
137 vec2 tileSize = iResolution.xy / downSampleFactor;
138 vec2 localXY = mod(fragCoord, tileSize);
139 if (floor(fragCoord.x / tileSize.x) > 0.5 || floor(fragCoord.y / tileSize.y) > 0.5) {
140 return vec4(0.0);
141 }
142 vec2 uv = (localXY + 0.5) / tileSize;
143 float dist = 1.2 - uv.y; // 1.2: origin height of the vertical blur
144 float blurRadius = mix(0.0, 0.3, smoothstep(0.0, 1.2, dist)); // 0.3: blur radius on top, 1.2: origin
145 vec4 col = vec4(0.0);
146 float totalWeight = 0.0;
147 for (int i = 0; i < sampleCount; ++i) {
148 float s = float(i) / float(sampleCount);
149 vec2 offset = vec2(0.0, s * blurRadius);
150 vec2 sampleUV = uv + offset;
151 sampleUV = clamp(sampleUV, vec2(0.0), vec2(1.0));
152 vec2 sampleCoord = sampleUV * iResolution.xy;
153 float weight = 1.0 - abs(s);
154 col += SampleWholeTile(sampleCoord, iResolution.xy) * weight;
155 totalWeight += weight;
156 }
157 return col / totalWeight;
158 }
159 )";
160 } // anonymous namespace
161
GEAuroraNoiseShader()162 GEAuroraNoiseShader::GEAuroraNoiseShader() {}
163
GEAuroraNoiseShader(Drawing::GEAuroraNoiseShaderParams & param)164 GEAuroraNoiseShader::GEAuroraNoiseShader(Drawing::GEAuroraNoiseShaderParams& param)
165 {
166 auroraNoiseParams_ = param;
167 }
168
CreateAuroraNoiseShader(Drawing::GEAuroraNoiseShaderParams & param)169 std::shared_ptr<GEAuroraNoiseShader> GEAuroraNoiseShader::CreateAuroraNoiseShader(
170 Drawing::GEAuroraNoiseShaderParams& param)
171 {
172 std::shared_ptr<GEAuroraNoiseShader> auroraNoiseShader = std::make_shared<GEAuroraNoiseShader>(param);
173 return auroraNoiseShader;
174 }
175
MakeDrawingShader(const Drawing::Rect & rect,float progress)176 void GEAuroraNoiseShader::MakeDrawingShader(const Drawing::Rect& rect, float progress)
177 {
178 drShader_ = MakeAuroraNoiseShader(rect);
179 }
180
Preprocess(Drawing::Canvas & canvas,const Drawing::Rect & rect)181 void GEAuroraNoiseShader::Preprocess(Drawing::Canvas& canvas, const Drawing::Rect& rect)
182 {
183 Drawing::ImageInfo downSampledImg(rect.GetWidth(), rect.GetHeight(),
184 Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
185 noiseImg_ = MakeAuroraNoiseGeneratorShader(canvas, downSampledImg);
186 Drawing::ImageInfo verticalBlurImgInf(rect.GetWidth(), rect.GetHeight(),
187 Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
188 verticalBlurImg_ = MakeAuroraNoiseVerticalBlurShader(canvas, verticalBlurImgInf);
189 }
190
MakeAuroraNoiseGeneratorShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)191 std::shared_ptr<Drawing::Image> GEAuroraNoiseShader::MakeAuroraNoiseGeneratorShader(
192 Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
193 {
194 float width = imageInfo.GetWidth();
195 float height = imageInfo.GetHeight();
196 builder_ = GetAuroraNoiseBuilder();
197 builder_->SetUniform("iResolution", width, height);
198 builder_->SetUniform("noise", auroraNoiseParams_.noise_);
199 auto auroraNoiseGeneratorShader = builder_->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
200 if (auroraNoiseGeneratorShader == nullptr) {
201 GE_LOGE("GEAuroraNoiseShader auroraNoiseGeneratorShader is nullptr.");
202 return nullptr;
203 }
204 return auroraNoiseGeneratorShader;
205 }
206
MakeAuroraNoiseVerticalBlurShader(Drawing::Canvas & canvas,const Drawing::ImageInfo & imageInfo)207 std::shared_ptr<Drawing::Image> GEAuroraNoiseShader::MakeAuroraNoiseVerticalBlurShader(
208 Drawing::Canvas& canvas, const Drawing::ImageInfo& imageInfo)
209 {
210 if (noiseImg_ == nullptr) {
211 GE_LOGE("GEAuroraNoiseShader MakeAuroraNoiseVerticalBlurShader noiseImg_ is nullptr.");
212 return nullptr;
213 }
214 float width = imageInfo.GetWidth();
215 float height = imageInfo.GetHeight();
216 verticalBlurBuilder_ = GetAuroraNoiseVerticalBlurBuilder();
217 if (verticalBlurBuilder_ == nullptr) {
218 GE_LOGE("GEAuroraNoiseShader::MakeAuroraNoiseVerticalBlurShader verticalBlurBuilder_ is nullptr.");
219 return nullptr;
220 }
221 Drawing::Matrix matrix;
222 auto auroraNoiseShader = Drawing::ShaderEffect::CreateImageShader(*noiseImg_,
223 Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
224 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
225 verticalBlurBuilder_->SetChild("auroraNoiseTexture", auroraNoiseShader);
226 verticalBlurBuilder_->SetUniform("iResolution", width, height);
227 auto auroraNoiseVerticalBlurShader =
228 verticalBlurBuilder_->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
229 if (auroraNoiseVerticalBlurShader == nullptr) {
230 GE_LOGE("GEAuroraNoiseShader::MakeAuroraNoiseVerticalBlurShader auroraNoiseVerticalBlurShader is nullptr.");
231 return nullptr;
232 }
233 return auroraNoiseVerticalBlurShader;
234 }
235
GetAuroraNoiseBuilder()236 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEAuroraNoiseShader::GetAuroraNoiseBuilder()
237 {
238 thread_local std::shared_ptr<Drawing::RuntimeEffect> auroraNoiseShaderEffect_ = nullptr;
239 if (auroraNoiseShaderEffect_ == nullptr) {
240 auroraNoiseShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(AURORA_GENERATOR_PROG);
241 }
242
243 if (auroraNoiseShaderEffect_ == nullptr) {
244 GE_LOGE("GEAuroraNoiseShader::GetAuroraNoiseBuilder auroraNoiseShaderEffect_ is nullptr.");
245 return nullptr;
246 }
247 return std::make_shared<Drawing::RuntimeShaderBuilder>(auroraNoiseShaderEffect_);
248 }
249
GetAuroraNoiseVerticalBlurBuilder()250 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEAuroraNoiseShader::GetAuroraNoiseVerticalBlurBuilder()
251 {
252 thread_local std::shared_ptr<Drawing::RuntimeEffect> auroraNoiseVerticalBlurShaderEffect_ = nullptr;
253
254 if (auroraNoiseVerticalBlurShaderEffect_ == nullptr) {
255 auroraNoiseVerticalBlurShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(AURORA_VERT_BLUR_PROG);
256 }
257
258 if (auroraNoiseVerticalBlurShaderEffect_ == nullptr) {
259 GE_LOGE("GEAuroraNoiseShader::GetAuroraNoiseBuilder auroraNoiseVerticalBlurShaderEffect_ is nullptr.");
260 return nullptr;
261 }
262 return std::make_shared<Drawing::RuntimeShaderBuilder>(auroraNoiseVerticalBlurShaderEffect_);
263 }
264
GetAuroraNoiseUpSamplingBuilder()265 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEAuroraNoiseShader::GetAuroraNoiseUpSamplingBuilder()
266 {
267 thread_local std::shared_ptr<Drawing::RuntimeEffect> auroraNoiseUpSamplingShaderEffect_ = nullptr;
268 if (auroraNoiseUpSamplingShaderEffect_ == nullptr) {
269 static constexpr char prog[] = R"(
270 uniform shader verticalBlurTexture;
271 uniform vec2 iResolution;
272
273 const float downSampleFactor = 8.0;
274
275 vec4 main(vec2 fragCoord)
276 {
277 vec2 tileSize = iResolution.xy / downSampleFactor;
278 vec2 uvInTile = fragCoord / iResolution.xy;
279 vec2 pixel = uvInTile * (tileSize - 1.0);
280 return verticalBlurTexture.eval(pixel + 0.5);
281 }
282 )";
283 auroraNoiseUpSamplingShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(prog);
284 }
285
286 if (auroraNoiseUpSamplingShaderEffect_ == nullptr) {
287 GE_LOGE("GEAuroraNoiseShader auroraNoiseUpSamplingShaderEffect_ is nullptr.");
288 return nullptr;
289 }
290 return std::make_shared<Drawing::RuntimeShaderBuilder>(auroraNoiseUpSamplingShaderEffect_);
291 }
292
MakeAuroraNoiseShader(const Drawing::Rect & rect)293 std::shared_ptr<Drawing::ShaderEffect> GEAuroraNoiseShader::MakeAuroraNoiseShader(const Drawing::Rect& rect)
294 {
295 if (verticalBlurImg_ == nullptr) {
296 GE_LOGE("GEAuroraNoiseShader MakeAuroraNoiseShader verticalBlurImg_ is nullptr.");
297 return nullptr;
298 }
299 auto width = rect.GetWidth();
300 auto height = rect.GetHeight();
301 Drawing::Matrix matrix;
302 auto verticalBlurShader = Drawing::ShaderEffect::CreateImageShader(*verticalBlurImg_, Drawing::TileMode::CLAMP,
303 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
304 upSamplingBuilder_ = GetAuroraNoiseUpSamplingBuilder();
305 if (upSamplingBuilder_ == nullptr) {
306 GE_LOGE("GEAuroraNoiseShader::MakeAuroraNoiseShader upSamplingBuilder_ is nullptr.");
307 return nullptr;
308 }
309 upSamplingBuilder_->SetChild("verticalBlurTexture", verticalBlurShader);
310 upSamplingBuilder_->SetUniform("iResolution", width, height);
311 auto auroraNoiseUpSamplingShader = upSamplingBuilder_->MakeShader(nullptr, false);
312 if (auroraNoiseUpSamplingShader == nullptr) {
313 GE_LOGE("GEAuroraNoiseShader::MakeAuroraNoiseShader auroraNoiseUpSamplingShader is nullptr.");
314 return nullptr;
315 }
316 return auroraNoiseUpSamplingShader;
317 }
318
319 } // namespace Rosen
320 } // namespace OHOS