• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_log.h"
16 #include "ge_border_light_shader.h"
17 #include "ge_visual_effect_impl.h"
18 
19 namespace OHOS {
20 namespace Rosen {
21 
22 constexpr size_t NUM_0 = 0;
23 constexpr size_t NUM_1 = 1;
24 constexpr size_t NUM_2 = 2;
25 constexpr size_t NUM_3 = 3;
26 constexpr size_t NUM_4 = 4;
27 
GEBorderLightShader()28 GEBorderLightShader::GEBorderLightShader() {}
29 
GEBorderLightShader(Drawing::GEBorderLightShaderParams & param)30 GEBorderLightShader::GEBorderLightShader(Drawing::GEBorderLightShaderParams& param)
31 {
32     borderLightParams_ = param;
33 }
34 
MakeDrawingShader(const Drawing::Rect & rect,float progress)35 void GEBorderLightShader::MakeDrawingShader(const Drawing::Rect& rect, float progress)
36 {
37     drShader_ = MakeBorderLightShader(rect);
38 }
39 
GetBorderLightBuilder()40 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEBorderLightShader::GetBorderLightBuilder()
41 {
42     thread_local std::shared_ptr<Drawing::RuntimeEffect> borderLightShaderEffect_ = nullptr;
43 
44     if (borderLightShaderEffect_ == nullptr) {
45         static constexpr char prog[] = R"(
46             uniform half2 iResolution;
47             uniform half3 lightPosition;
48             uniform half4 lightColor;
49             uniform half lightIntensity;
50             uniform half lightWidth;
51             uniform half3 borderLightRotationAngle;
52             uniform half cornerRadius;
53 
54             half sdRoundedBox(half2 p, half2 b, half r)
55             {
56                 half2 q = abs(p) - b + r;
57                 return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
58             }
59 
60             half2 sdRoundedBoxGradient(half2 p, half2 b, half r)
61             {
62                 half2 signs = half2(p.x >= 0.0 ? 1.0 : -1.0, p.y >= 0.0 ? 1.0 : -1.0);
63                 half2 q = abs(p) - b + r;
64                 half2 nor = (q.y > q.x) ? half2(0.0, 1.0) : half2(1.0, 0.0);
65                 nor = (q.x > 0.0 && q.y > 0.0) ? normalize(q) : nor;
66                 return signs * nor;
67             }
68 
69             half4 shinningEffect(in half3 fragPos, in half3 normal, in half3 lightPos, in half3 viewPos,
70                 in half4 specularColor, in half shinning)
71             {
72                 half3 lightDir = normalize(lightPos - fragPos);
73                 half3 viewDir = normalize(viewPos - fragPos);
74                 half3 halfwayDir = normalize(lightDir + viewDir);
75 
76                 // Blinn-Phong
77                 half4 specularC = specularColor * pow(max(dot(normal, halfwayDir), 0.), shinning);
78 
79                 return specularC;
80             }
81 
82             half4 createBoundNormal(half2 pos) // pos in [-w, w] x [-1, 1]
83             {
84                 half2 halfWH = half2(iResolution.x, iResolution.y) / iResolution.y;
85                 half r = min(cornerRadius / iResolution.y * 2.0, min(halfWH.x, halfWH.y));
86                 half dist = sdRoundedBox(pos, halfWH, r);
87                 half thickness = min(lightWidth, cornerRadius) / iResolution.y;
88                 if (dist >= 0.0 || dist <= -thickness) {
89                     return half4(0.0, 0.0, 1.0, -1.0);
90                 }
91 
92                 half R = thickness * 9.0;  // 9.0 is adjustable parameters
93                 half sinTheta = abs(dist) / R;
94 
95                 half normalZ = sqrt(1.0 - sinTheta * sinTheta);
96                 half2 normalXY = sdRoundedBoxGradient(pos, halfWH, r) * sinTheta;
97 
98                 return half4(normalXY, normalZ, dist / thickness + 1.0);
99             }
100 
101             half4 RoundedBoxShinning(half2 uv, half4 specularColor, half shinning, half3 lightPos,
102                 half3 viewPos, mat3 rotM)
103             {
104                 half3 fragPos = half3(uv, 0.0);
105                 half4 normal = createBoundNormal(uv);
106                 if (normal.w < 0.0) {
107                     return half4(0.0);
108                 }
109                 half3 fragNormal = rotM * normal.xyz;
110 
111                 half4 shinningColor = shinningEffect(fragPos, fragNormal, lightPos, viewPos, specularColor, shinning);
112                 shinningColor *= smoothstep(0.0, 0.1, normal.w); // edge smooth
113                 return shinningColor;
114             }
115 
116             mat3 GetRotationMatrix(half3 rotAngle)
117             {
118                 rotAngle *= 3.1415926 / 180.0;
119                 mat3 Rx = mat3 (1.0, 0.0, 0.0,
120                                 0.0, cos(rotAngle.x), -sin(rotAngle.x),
121                                 0.0, sin(rotAngle.x), cos(rotAngle.x));
122                 mat3 Ry = mat3 (cos(rotAngle.y), 0.0, sin(rotAngle.y),
123                                 0.0, 1.0, 0.0,
124                                 -sin(rotAngle.y), 0.0, cos(rotAngle.y));
125                 mat3 Rz = mat3 (cos(rotAngle.z), -sin(rotAngle.z), 0.0,
126                                 sin(rotAngle.z), cos(rotAngle.z), 0.0,
127                                 0.0, 0.0, 1.0);
128                 return Rz * Ry * Rx;
129             }
130 
131             half4 main(vec2 fragCoord)
132             {
133                 half2 uv = fragCoord / iResolution.xy;
134                 uv = uv + uv - 1.0;
135                 half screenRatio = iResolution.x / iResolution.y;
136                 uv.x *= screenRatio;
137 
138                 half4 shinningColor = half4(0.0);
139                 half3 lightPos = half3(lightPosition.x * screenRatio, lightPosition.y, lightPosition.z);
140 
141                 if (dot(borderLightRotationAngle, borderLightRotationAngle) < 0.01) {
142                     half r = min(cornerRadius * 2.0, min(iResolution.x, iResolution.y));
143                     half2 q = abs(fragCoord + fragCoord - iResolution.xy) - iResolution.xy + r;
144                     half dist = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
145                     half normalizedDist = dist / (lightWidth + 0.001) + 1.0;
146                     shinningColor = (normalizedDist > 0.0 && normalizedDist < 1.0)
147                         ? lightColor * pow(max(normalize(lightPos - half3(uv, 0.0)).z, 0.0), 16.0) : half4(0.0);
148                     shinningColor *= smoothstep(0.0, 0.1, normalizedDist); // edge smooth
149                 } else {
150                     mat3 rotM = GetRotationMatrix(borderLightRotationAngle);
151                     half4 specularColor = lightColor;
152                     half shinning = 16.0;
153                     half3 viewPos = lightPos;
154                     shinningColor = RoundedBoxShinning(uv, specularColor, shinning, lightPos, viewPos, rotM);
155                 }
156 
157                 return half4(shinningColor.xyz * lightColor.w, shinningColor.w) * clamp(lightIntensity, 0.0, 1.0);
158             }
159         )";
160         borderLightShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(prog);
161     }
162 
163     if (borderLightShaderEffect_ == nullptr) {
164         GE_LOGE("GEBorderLightShader::GetBorderLightBuilder borderLightShaderEffect_ is nullptr.");
165         return nullptr;
166     }
167     return std::make_shared<Drawing::RuntimeShaderBuilder>(borderLightShaderEffect_);
168 }
169 
MakeBorderLightShader(const Drawing::Rect & rect)170 std::shared_ptr<Drawing::ShaderEffect> GEBorderLightShader::MakeBorderLightShader(const Drawing::Rect& rect)
171 {
172     auto width = rect.GetWidth();
173     auto height = rect.GetHeight();
174     float lightColor[NUM_4] = {borderLightParams_.color[NUM_0], borderLightParams_.color[NUM_1],
175         borderLightParams_.color[NUM_2], borderLightParams_.color[NUM_3]};
176     builder_ = GetBorderLightBuilder();
177     builder_->SetUniform("iResolution", width, height);
178     builder_->SetUniform("lightPosition", borderLightParams_.position[NUM_0],
179         borderLightParams_.position[NUM_1], borderLightParams_.position[NUM_2]);
180     builder_->SetUniform("lightColor", lightColor, NUM_4);
181     builder_->SetUniform("lightIntensity", borderLightParams_.intensity);
182     builder_->SetUniform("lightWidth", borderLightParams_.width);
183     builder_->SetUniform("borderLightRotationAngle", borderLightParams_.rotationAngle[NUM_0],
184         borderLightParams_.rotationAngle[NUM_1], borderLightParams_.rotationAngle[NUM_2]);
185     builder_->SetUniform("cornerRadius", borderLightParams_.cornerRadius);
186     auto borderLightShader = builder_->MakeShader(nullptr, false);
187 
188     if (borderLightShader == nullptr) {
189         GE_LOGE("GEBorderLightShader::MakeBorderLightShader borderLightShader is nullptr.");
190         return nullptr;
191     }
192     return borderLightShader;
193 }
194 
195 } // namespace Rosen
196 } // namespace OHOS