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