diff --git a/BUILD.gn b/BUILD.gn index 217cb3cee6a87682ebc4c2a7ee2d1ea50379328e..a504ebe0984c31c4e5b31f68aa3559922124c18e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -61,6 +61,7 @@ ohos_source_set("graphics_effect_src") { "src/ge_aibar_shader_filter.cpp", "src/ge_bezier_warp_shader_filter.cpp", "src/ge_color_gradient_shader_filter.cpp", + "src/ge_content_light_shader_filter.cpp", "src/ge_displacement_distort_shader_filter.cpp", "src/ge_edge_light_shader_filter.cpp", "src/ge_external_dynamic_loader.cpp", diff --git a/include/ge_content_light_shader_filter.h b/include/ge_content_light_shader_filter.h new file mode 100644 index 0000000000000000000000000000000000000000..bb2676bcc77350cde04d34b821132a563f2a44b5 --- /dev/null +++ b/include/ge_content_light_shader_filter.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef GRAPHICS_EFFECT_GE_CONTENT_LIGHT_FILTER_H +#define GRAPHICS_EFFECT_GE_CONTENT_LIGHT_FILTER_H + +#include + +#include "ge_shader_filter.h" +#include "ge_visual_effect.h" + +#include "draw/canvas.h" +#include "effect/color_filter.h" +#include "effect/runtime_effect.h" +#include "effect/runtime_shader_builder.h" +#include "image/image.h" +#include "utils/matrix.h" +#include "utils/rect.h" + +namespace OHOS { +namespace Rosen { +class GEContentLightFilter : public GEShaderFilter { +public: + GE_EXPORT GEContentLightFilter(const Drawing::GEContentLightFilterParams& params); + ~GEContentLightFilter() override = default; + + GE_EXPORT std::shared_ptr ProcessImage(Drawing::Canvas &canvas, + const std::shared_ptr image, const Drawing::Rect &src, const Drawing::Rect &dst) override; + +private: + std::shared_ptr GetContentLightEffect(); + + Vector3f lightPosition_; + Vector4f lightColor_; + float lightIntensity_; + Drawing::Matrix matrix_; +}; + +} // namespace Rosen +} // namespace OHOS + +#endif // GRAPHICS_EFFECT_GE_CONTENT_LIGHT_FILTER_H \ No newline at end of file diff --git a/include/ge_shader_filter_params.h b/include/ge_shader_filter_params.h index a60e6e15b7b4da6622ebecd00df5d095e3f6e563..1a9bd84fb926b36d1e4bb97d6cc7a202c30ef6f1 100644 --- a/include/ge_shader_filter_params.h +++ b/include/ge_shader_filter_params.h @@ -19,6 +19,7 @@ #include #include +#include "common/rs_vector3.h" #include "common/rs_vector4.h" #include "utils/matrix.h" #include "ge_shader_mask.h" @@ -282,6 +283,18 @@ struct GERadialGradientShaderMaskParams { std::vector positions_; }; +constexpr char GE_FILTER_CONTENT_LIGHT[] = "CONTENT_LIGHT"; +constexpr char GE_FILTER_CONTENT_LIGHT_POSITION[] = "CONTENT_LIGHT_POSITION"; +constexpr char GE_FILTER_CONTENT_LIGHT_COLOR[] = "CONTENT_LIGHT_COLOR"; +constexpr char GE_FILTER_CONTENT_LIGHT_INTENSITY[] = "CONTENT_LIGHT_INTENSITY"; +constexpr char GE_FILTER_CONTENT_LIGHT_MATRIX[] = "CONTENT_LIGHT_MATRIX"; +struct GEContentLightFilterParams { + Vector3f lightPosition; + Vector4f lightColor; + float lightIntensity; + Drawing::Matrix matrix; +}; + } // namespace Drawing } // namespace Rosen } // namespace OHOS diff --git a/include/ge_visual_effect.h b/include/ge_visual_effect.h index 33bd83b777a19e6977a5d5ab9f0fffc02ec8804c..a15da16a0fd2694cea0b597ee29b420c8210739f 100644 --- a/include/ge_visual_effect.h +++ b/include/ge_visual_effect.h @@ -18,6 +18,7 @@ #include #include +#include "common/rs_vector3.h" #include "common/rs_vector4.h" #include "effect/color_filter.h" #include "effect/runtime_effect.h" @@ -58,6 +59,8 @@ public: void SetParam(const std::string& tag, const std::vector& param); void SetParam(const std::string& tag, const std::shared_ptr param); void SetParam(const std::string& tag, const Drawing::Color4f& param); + void SetParam(const std::string& tag, const Vector3f& param); + void SetParam(const std::string& tag, const Vector4f& param); const std::string& GetName() const { diff --git a/include/ge_visual_effect_impl.h b/include/ge_visual_effect_impl.h index 337666ebda23dc31fd23a770cb0dde3e86d78f54..c6d1932dff7a82bf55a9940c7a4692aff18b1acf 100644 --- a/include/ge_visual_effect_impl.h +++ b/include/ge_visual_effect_impl.h @@ -23,6 +23,8 @@ #include "ge_shader_filter_params.h" #include "ge_visual_effect.h" +#include "common/rs_vector3.h" +#include "common/rs_vector4.h" #include "effect/color_filter.h" #include "effect/runtime_effect.h" #include "effect/runtime_shader_builder.h" @@ -56,6 +58,7 @@ public: EDGE_LIGHT, BEZIER_WARP, DISPERSION, + CONTENT_LIGHT, MAX }; @@ -80,6 +83,8 @@ public: void SetParam(const std::string& tag, const std::vector& param); void SetParam(const std::string& tag, const std::shared_ptr param); void SetParam(const std::string& tag, const Drawing::Color4f& param); + void SetParam(const std::string& tag, const Vector3f& param); + void SetParam(const std::string& tag, const Vector4f& param); void SetFilterType(FilterType type) { @@ -221,6 +226,16 @@ public: return dispersionParams_; } + void MakeContentLightParams() + { + contentLightParams_ = std::make_shared(); + } + + const std::shared_ptr& GetContentLightParams() const + { + return contentLightParams_; + } + private: static std::map> g_initialMap; @@ -238,6 +253,7 @@ private: void SetSoundWaveParamsFloat(const std::string& tag, float param); void SetEdgeLightParams(const std::string& tag, float param); void SetDispersionParams(const std::string& tag, float param); + void SetContentLightParams(const std::string& tag, float param); FilterType filterType_ = GEVisualEffectImpl::FilterType::NONE; @@ -259,6 +275,7 @@ private: std::shared_ptr edgeLightParams_ = nullptr; std::shared_ptr bezierWarpParams_ = nullptr; std::shared_ptr dispersionParams_ = nullptr; + std::shared_ptr contentLightParams_ = nullptr; }; } // namespace Drawing diff --git a/src/ge_content_light_shader_filter.cpp b/src/ge_content_light_shader_filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..69e1846f96cfe20a1611d270d18c07ecd1ff8d2c --- /dev/null +++ b/src/ge_content_light_shader_filter.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "ge_log.h" +#include "ge_content_light_shader_filter.h" + +namespace OHOS { +namespace Rosen { + +constexpr size_t NUM_0 = 0; +constexpr size_t NUM_1 = 1; +constexpr size_t NUM_2 = 2; +constexpr size_t NUM_3 = 3; +constexpr size_t NUM_4 = 4; + +GEContentLightFilter::GEContentLightFilter(const Drawing::GEContentLightFilterParams& params) + : lightPosition_(params.lightPosition), lightColor_(params.lightColor), lightIntensity_(params.lightIntensity), + matrix_(params.matrix) +{} + +std::shared_ptr GEContentLightFilter::ProcessImage(Drawing::Canvas& canvas, + const std::shared_ptr image, const Drawing::Rect& src, const Drawing::Rect& dst) +{ + if (image == nullptr) { + LOGE("GEContentLightFilter::ProcessImage input is invalid"); + return nullptr; + } + + Drawing::Matrix matrix; + auto shader = Drawing::ShaderEffect::CreateImageShader(*image, Drawing::TileMode::CLAMP, + Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix); + auto imageInfo = image->GetImageInfo(); + float height = imageInfo.GetHeight(); + float width = imageInfo.GetWidth(); + matrix_ = canvas.GetTotalMatrix(); + if (height < 1e-6 || width < 1e-6) { + return nullptr; + } + auto contentLight = GetContentLightEffect(); + if (contentLight == nullptr) { + LOGE("GEContentLightFilter::ProcessImage g_contentLightEffect init failed"); + return nullptr; + } + float lightColor[NUM_4] = {lightColor_[NUM_0], lightColor_[NUM_1], lightColor_[NUM_2], lightColor_[NUM_3]}; + Drawing::RuntimeShaderBuilder builder(contentLight); + builder.SetChild("image", shader); + builder.SetUniform("iResolution", width, height); + builder.SetUniform("lightIntensity", lightIntensity_); + builder.SetUniform("lightPosition", lightPosition_[NUM_0], lightPosition_[NUM_1], lightPosition_[NUM_2]); + builder.SetUniform("lightColor", lightColor, 4); + builder.SetUniform("matrix", matrix_); +#ifdef RS_ENABLE_GPU + auto invertedImage = builder.MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false); +#else + auto invertedImage = builder.MakeImage(nullptr, nullptr, imageInfo, false); +#endif + if (invertedImage == nullptr) { + LOGE("GEContentLightFilter::ProcessImage make image failed"); + return nullptr; + } + return invertedImage; +} + + +std::shared_ptr GEContentLightFilter::GetContentLightEffect() +{ + static std::shared_ptr contentLightEffect = nullptr; + if (contentLightEffect == nullptr) { + const std::string shaderStringContentLight = R"( + uniform shader image; + uniform half2 iResolution; + uniform half lightIntensity; + uniform half3 lightPosition; + uniform half4 lightColor; + uniform mat3 matrix; + + const float cornerRadius = 30.0; + const float boundaryThickness = 5.0; + + // 计算圆角矩形SDF + float sdRoundedBox(vec2 p, vec2 b, float r) + { + vec2 q = abs(p)-b+r; + return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r; + } + + vec4 shinningEffect(in vec3 fragPos, in vec3 normal, in vec3 lightPos, in vec3 viewPos, + in vec4 specularColor, in float shinning) + { + vec3 lightDir = normalize(lightPos - fragPos); + vec3 viewDir = normalize(viewPos - fragPos); + vec3 halfwayDir = normalize(lightDir + viewDir); + + // Blinn-Phong + vec4 specularC = specularColor * pow(max(dot(normal, halfwayDir), 0.), shinning); + + return specularC; + } + + vec4 createContentNormal(vec2 pos, float maskAlpha) // pos in [-w, w] x [-1, 1] + { + if (maskAlpha < 0.01) { + return vec4(0.0, 0.0, -1.0, -1.0); + } + + ///////////////////////////////////////////////////////// + // 球面半径 = 4.0 * max(height, width) + // 球顶: z = sqrt(R^2 - x^2 - y^2) + h + // (x0, y0) x切向导数:2x0 / z0 + // (x0, y0) y切向导数:2y0 / z0 + // (x0, y0) 法向:normalize(2x0 / z0, 2y0 / z0, 1) + float R = max(width, height) * 4. / iResolution.y; + float z = sqrt(R * R - pos.x * pos.x - pos.y * pos.y); + vec3 normal = normalize(vec3(pos.x / z, pos.y / z, 0.5)); + + return vec4(normal, 1.); + } + + vec4 ContentShinning(vec2 uv, vec4 specularColor, float shinning, vec3 lightPos, vec3 viewPos, mat3 rotM, float maskAlpha) + { + vec3 fragPos = vec3(uv, 0.0); + vec4 normal = createContentNormal(uv, maskAlpha); + if (normal.w < 0.0) { + return vec4(0.0); + } + vec3 fragNormal = rotM * normal.xyz; + + vec4 shinningColor = shinningEffect(fragPos, fragNormal, lightPos, viewPos, specularColor, shinning); + // shinningColor *= smoothstep(0.0, 0.1, normal.w) * smoothstep(1.0, 0.9, normal.w); + return shinningColor; + } + + // 根据旋转前后法向量 计算旋转矩阵 + mat3 rotationMatrix(in vec3 originNormal, in vec3 centerNormal) + { + vec3 k = cross(originNormal, centerNormal); + mat3 R = mat3(1., 0., 0., 0., 1., 0., 0., 0., 1.); + if (length(k) < 1e-6) // 未区分平行和反向 + return R; + + float theta = acos(dot(originNormal, centerNormal)); + mat3 K = mat3(0., -k.z, k.y, + k.z, 0., -k.x, + -k.y, k.x, 0.); + R += sin(theta) * K + (1. - cos(theta)) * (K * K); + + return R; + } + + // test example + vec4 chessboardColor(in vec2 uv) + { + // random color + vec3 col = 0.5 + 0.5 * cos(uv.xyx + vec3(-1.5, 2.0, 4.0)); + // chessboard + float num = floor(uv.x * 5.0) + floor(uv.y * 5.0); + + return vec4(col, mod(num, 2.0)); + } + + // test example + vec4 DrawSmoothRRect(vec2 uv, vec2 center, vec2 halfWidthHeight, float radius) + { + radius = clamp(radius, 0.0, min(halfWidthHeight.x, halfWidthHeight.y)); + float dist = sdRoundedBox(uv - center, halfWidthHeight, radius); + + vec4 color = chessboardColor(uv); + + return dist < 0.0 ? smoothstep(0.0, -2.5 / iResolution.y, dist) * color : vec4(0.0); + } + + vec4 main(in vec2 fragCoord) + { + vec2 uv = fragCoord / iResolution.xy; + uv = uv + uv - 1.0; + float screenRatio = iResolution.x / iResolution.y; + uv.x *= screenRatio; + + float alpha = 0.7; + mat3 rotM = matrix; + vec4 specularColor = lightColor; + float shinning = 8.0; + vec3 lightPos = lightPosition; + + vec3 viewPos = lightPos; + + vec4 inputImage = image.eval(fragCoord); + vec4 shinningColor = ContentShinning(uv, specularColor, shinning, lightPos, viewPos, rotM, inputImage.w); + + return vec4(inputImage.rgb + shinningColor.rgb * lightIntensity, 1.0); + } + + )"; + contentLightEffect = Drawing::RuntimeEffect::CreateForShader(shaderStringContentLight); + } + return contentLightEffect; +} +} // namespace Rosen +} // namespace OHOS \ No newline at end of file diff --git a/src/ge_render.cpp b/src/ge_render.cpp index 95d05eecc4c13ee43239519b0bd38ddffaa54fe1..ac8540f7ae6eee399b1440ad8f53bf57142ee1ee 100644 --- a/src/ge_render.cpp +++ b/src/ge_render.cpp @@ -29,6 +29,7 @@ #include "ge_sound_wave_filter.h" #include "ge_external_dynamic_loader.h" #include "ge_edge_light_shader_filter.h" +#include "ge_content_light_shader_filter.h" namespace OHOS { @@ -204,6 +205,11 @@ std::vector> GERender::GenerateShaderFilter( shaderFilter = GenerateExtShaderFilter(ve); break; } + case Drawing::GEVisualEffectImpl::FilterType::CONTENT_LIGHT: { + const auto& contentLightParams = ve->GetContentLightParams(); + shaderFilter = std::make_shared(*contentLightParams); + break; + } default: break; } diff --git a/src/ge_visual_effect.cpp b/src/ge_visual_effect.cpp index 39579e839c58fcad42e064ad1bafb4ef0a9614c6..8f7b832fc44a8fe74218d85598ee5a63dd9316ee 100644 --- a/src/ge_visual_effect.cpp +++ b/src/ge_visual_effect.cpp @@ -99,6 +99,16 @@ void GEVisualEffect::SetParam(const std::string& tag, const Drawing::Color4f& pa visualEffectImpl_->SetParam(tag, param); } +void GEVisualEffect::SetParam(const std::string& tag, const Vector3f& param) +{ + visualEffectImpl_->SetParam(tag, param); +} + +void GEVisualEffect::SetParam(const std::string& tag, const Vector4f& param) +{ + visualEffectImpl_->SetParam(tag, param); +} + } // namespace Drawing } // namespace Rosen } // namespace OHOS diff --git a/src/ge_visual_effect_impl.cpp b/src/ge_visual_effect_impl.cpp index b72e63ab56f36c5727e1c77cf8f85e7424225718..9310eb5d3eab93d50f0dc19cb76042b520d25663 100644 --- a/src/ge_visual_effect_impl.cpp +++ b/src/ge_visual_effect_impl.cpp @@ -19,6 +19,7 @@ #include "ge_log.h" #include "ge_external_dynamic_loader.h" #include "common/rs_vector4.h" +#include "common/rs_vector3.h" namespace OHOS { namespace Rosen { @@ -102,6 +103,12 @@ std::map> GEVisualEf impl->SetFilterType(GEVisualEffectImpl::FilterType::DISPERSION); impl->MakeDispersionParams(); } + }, + { GE_FILTER_CONTENT_LIGHT, + [](GEVisualEffectImpl* impl) { + impl->SetFilterType(GEVisualEffectImpl::FilterType::CONTENT_LIGHT); + impl->MakeContentLightParams(); + } } }; @@ -250,6 +257,10 @@ void GEVisualEffectImpl::SetParam(const std::string& tag, float param) SetDispersionParams(tag, param); break; } + case FilterType::CONTENT_LIGHT: { + SetContentLightParams(tag, param); + break; + } default: break; } @@ -276,6 +287,16 @@ void GEVisualEffectImpl::SetParam(const std::string& tag, const Drawing::Matrix } break; } + case FilterType::CONTENT_LIGHT: { + if (contentLightParams_ == nullptr) { + return; + } + + if (tag == GE_FILTER_CONTENT_LIGHT_MATRIX) { + contentLightParams_->matrix = param; + } + break; + } default: break; } @@ -453,6 +474,40 @@ void GEVisualEffectImpl::SetParam(const std::string& tag, const std::shared_ptr< } } +void GEVisualEffectImpl::SetParam(const std::string& tag, const Vector3f& param) +{ + switch (filterType_) { + case FilterType::CONTENT_LIGHT: { + if (contentLightParams_ == nullptr) { + return; + } + if (tag == GE_FILTER_CONTENT_LIGHT_POSITION) { + contentLightParams_->lightPosition = param; + } + break; + } + default: + break; + } +} + +void GEVisualEffectImpl::SetParam(const std::string& tag, const Vector4f& param) +{ + switch (filterType_) { + case FilterType::CONTENT_LIGHT: { + if (contentLightParams_ == nullptr) { + return; + } + if (tag == GE_FILTER_CONTENT_LIGHT_COLOR) { + contentLightParams_->lightColor = param; + } + break; + } + default: + break; + } +} + void GEVisualEffectImpl::SetParam(const std::string& tag, const Drawing::Color4f& param) { switch (filterType_) { @@ -775,6 +830,23 @@ void GEVisualEffectImpl::SetDispersionParams(const std::string& tag, float param } } +void GEVisualEffectImpl::SetContentLightParams(const std::string& tag, float param) +{ + if (contentLightParams_ == nullptr) { + return; + } + + static std::unordered_map> actions = { + { GE_FILTER_CONTENT_LIGHT_INTENSITY, + [](GEVisualEffectImpl* obj, float p) { obj->contentLightParams_->lightIntensity = p; } }, + }; + + auto it = actions.find(tag); + if (it != actions.end()) { + it->second(this, param); + } +} + } // namespace Drawing } // namespace Rosen } // namespace OHOS