Add GlassStyle with internal tent blur support.#1507
Conversation
…acement map support
…d ray-based normalization.
…nt normal, edge-band only refraction, actual maxDisp from two-pass computation.
…thickness multiplier
… add GlassStyleSingle test
…mediate texture quantization
…uared displacement
…-style pipeline - Replace GlassHighlightMap with GlassMaskEffect RGBA8 32-bit packing pass - Generate UDF height map by Gaussian-blurring layer alpha, controlled by depth - Implement Figma-style refraction: gradient direction, splay mixing, edge proximity offset - Add GlassShaderFragments.h with modular SDF shaders (RoundedRect, Ellipse, Star, AlphaMask) - Restore radial+axis direction mixing for analytical SDF shapes - Add smooth attenuation to prevent boundary discontinuities - Update GlassStyleSingle test to use star-shaped AlphaMask glass - Add figma-glass-alpha-mask-pipeline documentation
…nd docs - Add LiquidGlass builder with star-shaped AlphaMask glass + mouse drag support - Add checker_128.png resource to Xcode project - Add virtual setGlassPosition to LayerBuilder for mouse interaction - Simplify ior to direct refractionFactor (remove unnecessary ior variable) - Update blur sigma range to 0.2-0.4 based on depth - Update refDist formula to halfW * (0.5*refractionFactor + 0.5*depthRatio) - Use edge proximity (1-height) for AlphaMask offset distance - Add glass-style-rendering-pipeline.md documentation
- Add public getType(), getRRect(), getRect(), getOval() to LayerContent - Override getRRect() in RRectContent, getRect() in RectContent, getOval() in PathContent - Add layerContent field to LayerStyleInput - Pass getContent() into LayerStyleInput in Layer.cpp and BackgroundHandler.cpp - GlassStyle auto-detects shape type: RRect→RoundedRect, oval Path→Ellipse, other→AlphaMask - Update rendering pipeline documentation with detailed shader flow
- Replace 4-point central difference with 3x3 Sobel operator for smoother gradient direction - Keep adaptive step size (minHalf/20 + 1) without clamp - Temporarily set GlassStyleSingle test to refraction=1, depth=10 for debugging
- Fix RGBA8 pack shader: clamp value to 0.999999 before packing to avoid fract(1.0) precision issues that caused center regions to decode as 0.50 - Use 3x3 Sobel operator instead of 4-point central difference for smoother gradient direction in AlphaMask path - Change blur sigma formula to minHalf * (depth/100) * 0.2 - Update GlassStyleSingle test params to refraction=70, depth=80
- Change offsetDist from refDist*edgeProximity to refractionFactor*depthRatio*edgeProximity*halfW - Remove refDist variable, use halfW directly for clamp - Update GlassStyleSingle test to refraction=50, depth=100, splay=100
…U pipeline each frame.
…d rendering mode.
…d add edge lighting with tent blur UDF
…al MSAA and granular cache invalidation
| auto radii = rRect.radii(); | ||
| // The analytical SDF currently supports a single scalar corner radius. | ||
| float representativeRadius = std::min(radii[0].x, radii[0].y); | ||
| crRadius = std::min(representativeRadius * effectiveContentScale, minHalf); |
There was a problem hiding this comment.
crRadius 乘了 effectiveContentScale(内容像素空间),但 shader 里 SDF 是在 layer 空间求值的:glassPixel = glassUV * (halfW2, halfH2),halfW/halfH 来自未缩放的 origBounds。contentScale != 1(zoom)时 SDF 圆角半径与实际形状不匹配,且与上方“zoom-invariant”的注释意图矛盾。建议直接用 layer 空间半径:std::min(representativeRadius, minHalf)(212 行 _cornerRadius 分支同理)。
| // distance in layer space remains consistent regardless of UDF resolution. | ||
| float origMaxDim = std::max(origBounds.width(), origBounds.height()); | ||
| if (origMaxDim <= 0.0f) { | ||
| return; |
There was a problem hiding this comment.
当 layerContent 为 nullptr(例如 group 图层)时 origBounds 为 0x0,走到这里 origMaxDim <= 0 直接 return,整个玻璃效果(包括已经计算好的 frost 背景)都会静默不绘制。建议回退用 input.content 的尺寸推导 origBounds,或至少继续绘制磨砂背景而不是直接返回。
| } | ||
|
|
||
| /** Sets the corner radius for the glass surface shape. */ | ||
| void setCornerRadius(float radius); |
There was a problem hiding this comment.
cornerRadius/setCornerRadius 目前是死代码:_cornerRadius 唯一的读取点(GlassStyle.cpp:212)算出的值在所有可达分支都会被覆盖——RRect 分支改用实际 radii、Rect 分支置 0、Ellipse/AlphaMask 不使用。公开 API 无任何实际效果,建议删除,或让它真正生效(例如作为内容无半径信息时的覆盖值)。
|
|
||
| PlacementPtr<FragmentProcessor> sourceFragment = getSourceFragmentProcessor( | ||
| source, args.context, args.renderFlags, srcSampleBounds, Point(blurDstScaleX, blurDstScaleY)); | ||
| const auto isAlphaOnly = source->isAlphaOnly(); |
There was a problem hiding this comment.
blur 目标格式沿用了 source->isAlphaOnly(),但 TentBlur1D shader 输出的是 RGBA8 打包的 float。若源是 alpha-only(如 alpha8 图像内容),打包数据只剩 A 通道,第二 pass dot(rgba, UNPACK) 约等于 0,UDF/折射静默失效。打包路径应强制 RGBA8 目标(这里 isAlphaOnly 传 false)。
| root->addChild(container); | ||
| } | ||
|
|
||
| TGFX_TEST(LayerTest, GlassStyle) { |
There was a problem hiding this comment.
三个 GlassStyle 测试是同一套 7x3 参数扫描复制三遍,且解析 SDF 路径移除后 GlassShapeType 已只是测试侧的构造参数(三个测试走的是同一条 AlphaMask 路径),建议合并为一个参数化测试。另外所有用例都是 zoom=1:实现里多处依赖 contentScale/bgScale/udfScale 的 zoom-invariant 换算,缩放状态没有任何覆盖,坐标空间类问题无法被截图测试暴露。建议补一个缩放状态(如 setZoomScale(2))的 glass 截图用例。
| const bool hasCoarseMask = (coarseMaskProxy != nullptr); | ||
| const bool hasDispersion = (params.dispersion >= 0.01f); | ||
| const bool hasLightIntensity = (params.lightIntensity > 0.0f); | ||
| const bool useAxisMix = (params.shapeType == GlassShapeType::RoundedRect || |
There was a problem hiding this comment.
解析 SDF 路径从 GlassStyle 移除后,params.shapeType 在唯一生产处(getRefractionFilter)被硬编码为 AlphaMask,useAxisMix 相关的圆角矩形/椭圆 SDF 分支(约 100 行,含 81-106、126-134、158-198 行段)以及 GlassShapeType::RoundedRect/Ellipse 枚举值都已成为不可达死代码。这是被移除方案留下的脚手架,建议一并删掉:emitCode 只保留 AlphaMask 路径,GlassShapeType 枚举和 params.shapeType 字段也可以整体移除。
| float depthRatio = getDepthRatio(); | ||
| float glassThickness = getGlassThickness(minHalf); | ||
| float analyticalOffset = glassThickness * refractionFactor; | ||
| // Match the shader's AlphaMask max displacement: halfW * refractionFactor * depthRatio * |
There was a problem hiding this comment.
这轮 shader 已把 refractionDistance 改为 minHalf * refractionFactor * depthRatio * depthScale(edgeProximity 也从 h*(1-h) 改为 (1-h²)²),这里的注释还写的是 halfW,已过期。安全性没问题(minHalf <= halfW,outset 仍是上界),但建议同步更新注释;另外这个公式在 CPU 和 GLSL 各维护一份,已经是第二次漂移了,建议把 max displacement 的估算收敛到一处或加固定余量。
| " vec2 centerDir = (centerDistance > 0.001) ? vec2(-px / centerDistance, -py / " | ||
| "centerDistance) : vec2(0.0);"); | ||
| fragBuilder->codeAppend(" vec2 mixedDir = mix(gradientDir, centerDir, splay);"); | ||
| fragBuilder->codeAppend(" mixedDir = normalize(mixedDir);"); |
There was a problem hiding this comment.
这里 normalize(mixedDir) 存在除零的未定义行为:splay=1 且像素恰好落在形状中心时 centerDir=vec2(0),mixedDir=vec2(0),normalize(vec2(0)) 在 GLSL 里是 undefined,部分 GPU 会产生 NaN 坏点。虽然触发条件很窄(对称形状中心梯度≈0 会被外层 1e-6 阈值跳过),但建议拦截一下:normalize 前先判断 mixedDir 长度,如 float l = length(mixedDir); mixedDir = (l > 0.000001) ? mixedDir / l : gradientDir; 或直接跳过折射。
| * @param lightAngle The direction of the light source in degrees, range [-179, 180]. | ||
| * @param lightIntensity The brightness of edge highlights, range [0, 100]. | ||
| */ | ||
| static std::shared_ptr<GlassStyle> Make(float refraction = 80.0f, float depth = 20.0f, |
| * @param args The TPArgs used to create the texture proxy. | ||
| * @return The locked texture proxy, or nullptr on failure. | ||
| */ | ||
| static std::shared_ptr<TextureProxy> LockTextureProxy(const Image* image, const TPArgs& args); |
There was a problem hiding this comment.
fp不是拿来做LockTextureProxy的。你自己创建textureProxy,然后把fp fill上去。
There was a problem hiding this comment.
如果是拿image的,你也可以直接makeTextureImage,然后拿对应的texture
| @@ -0,0 +1,54 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////////////////// | |||
| @@ -0,0 +1,105 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////////////////// | |||
| @@ -0,0 +1,105 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////////////////// | |||
There was a problem hiding this comment.
TentBlurFilter(LayerImageFilter 包装层)在全仓库无任何使用方,TentBlurImageFilter 也只有 GlassStyle 生成 UDF 一个消费点,且其 lockTextureProxy 约 170 行处理的通用 ImageFilter 场景(drawScale 降采样、radius>64 钳制、needExtraTransform 重缩放)在 GlassStyle 固定调用约束下(源为刚缩放的位图、clipRect=全图、drawScale=1、radius<=60)全部不可达。这两个类是照 GaussianBlur 双层结构抄来的完整脚手架,建议都删掉,把双 pass tent blur 收进 GlassStyle 内部 helper:直接创建两个固定 RGBA8 的 RenderTargetProxy,用 TentBlur1DFragmentProcessor 做 Horizontal/Vertical 两次 fillRTWithFP 即可(约 30 行)。算法本体 TentBlur1DFragmentProcessor + GLSL 保留。顺带还能固定 RT 格式为 RGBA8,消掉 isAlphaOnly 跟随源导致打包失效的隐患。
| imageShader = imageShader->makeWithMatrix(imageMatrix); | ||
| paint.setShader(imageShader); | ||
|
|
||
| // Keep imageShader on paint so the refraction filter is inlined as a fragment processor. |
There was a problem hiding this comment.
这段注释描述的是旧的内联流程,已与现状相反:当前实现是先 makeWithFilter 把折射预光栅化成 refractedImage,再包进 imageShader——shader 上已经没有 filter 了,不存在“inlined as a fragment processor”,也没有 drawImageRect 的选择问题。建议删掉或改写为描述当前流程(预光栅化到低分辨率背景,再经 content alpha mask 绘制)。
| @@ -0,0 +1,48 @@ | |||
| ///////////////////////////////////////////////////////////////////////////////////////////////// | |||
There was a problem hiding this comment.
imageFilter建议单独定在一个文件夹里,不要和filters放一块
| // mask blur filter is cached for reuse. | ||
| // UDF is capped at MAX_UDF_SIZE to prevent excessive texture allocation. The mask UVs | ||
| // are normalized (0-1), so lower resolution does not affect the refraction shader. | ||
| static constexpr float MAX_UDF_SIZE = 512.0f; |
There was a problem hiding this comment.
可选优化(供作者评估,不要求本期处理):UDF 是被 tent blur 抹平的低频高度场,满足 scale 协变——可以参考高斯模糊 drawScale 的思路把 Fine UDF 从 512 降到 256(半径同步减半),shader 端本来就是归一化 UV + udfPixelToLayerPixel 换算,双线性上采样的误差对梯度计算几乎无感,UDF 链路成本可降到 1/4。也可以按 depth 动态调整:半径越大降得越多。
… makeTextureImage
…iolation and missing null checks.
| // Scale dispersion to [0, 0.2]: the shader offsets R/B UVs by uvOffset * (1 ± dispersion), | ||
| // so 0.2 means max 20% additional offset, keeping chromatic aberration subtle. | ||
| params.dispersion = (_dispersion / 100.0f) * 0.2f; | ||
| params.splay = _splay / 100.0f; |
There was a problem hiding this comment.
splay 没有在使用点 clamp:文档范围 [0,100],但 _splay 超出时 shader 里 mix(gradientDir, centerDir, splay) 变成外推,产生数学上无意义的折射方向(不是效果强弱问题)。同文件的 refraction/depth 都在使用点 clamp 了,这里建议保持一致:params.splay = std::clamp(_splay / 100.0f, 0.0f, 1.0f);
| void setDispersion(float value); | ||
|
|
||
| /** | ||
| * The spread of projected light across the glass surface. Range [0, 100]. |
There was a problem hiding this comment.
splay 的文档与当前实现不符(Make 的 @param 同问题):实现里 splay 是折射方向在 UDF 梯度方向与向心方向之间的混合因子(mix(gradientDir, centerDir, splay)),改变的是方向场,并不控制高光的宽度/扩散。这段描述疑似旧解析路径时期留下的,建议改为类似:The blend factor between the UDF gradient direction and the radial (center-pointing) direction used for refraction. Range [0, 100].
…lend implementation.
| * GlassStyle simulates the physical behavior of light passing through a glass surface, producing | ||
| * refraction, chromatic dispersion, frosted blur, and specular highlights. It captures the | ||
| * background content below the layer and renders it with optical distortion driven by a | ||
| * displacement map derived from the layer content's alpha mask. |
There was a problem hiding this comment.
公开 API 注释应面向调用方:displacement map derived from the layer content's alpha mask 暴露了内部实现机制(位移图/alpha mask),用户不需要知道。建议只描述行为和用法,例如:It captures the background content below the layer and renders it with optical distortion shaped by the layer's content.
| void setDispersion(float value); | ||
|
|
||
| /** | ||
| * The blend factor between the UDF gradient direction and the radial (center-pointing) |
There was a problem hiding this comment.
UDF 是内部实现术语,公开注释里用户无从理解(@param splay 一处同问题)。建议改成视觉效果描述,例如:The blend factor for the refraction direction. Range [0, 100]. At 0, refraction follows the curvature of the shape's edges; at 100, it points toward the shape center.
| /** Sets the refraction direction blend factor. */ | ||
| void setSplay(float value); | ||
|
|
||
| /** Light source direction in degrees. Range [-179, 180]. */ |
There was a problem hiding this comment.
角度缺少基准约定,用户无法预测光照方向。按当前实现(lightDir = (sin, cos),+Y 向下):0° 是正上方光源,正值顺时针旋转(90° 光源在左侧)。建议把约定写进注释,例如:Light source direction in degrees. 0 means light from directly above, positive values rotate clockwise. Range [-179, 180].
概述
本 PR 新增
GlassStyle图层样式,提供背景磨砂、边缘折射、色散和边缘光照效果;同时新增仅供 GlassStyle 内部生成 AlphaMask UDF 使用的三角模糊实现。主要改动
新增公开 API
GlassStyle新增玻璃渲染流程
新增 AlphaMask / UDF 路径
新增内部三角模糊
TentBlur1DFragmentProcessor仅位于src/layers/processors/,不作为公开 API。测试
GlassStyleEllipse、GlassStyleZoom两个截图测试覆盖椭圆和缩放场景。