Update highlight shader algorithm

This commit is contained in:
Kyant0 committed 2025-09-12 13:51:46 +08:00
1 parent 80562325cf
commit 78d4393908
2 files changed
+70 -26

No files matched your search

@@ -66,6 +66,43 @@ sealed interface GlassHighlight {
override val blendMode: BlendMode = BlendMode.Plus
) : GlassHighlight
/**
* Soft highlight effect with blur.
*
* @param width
* The width of the highlight.
*
* @param color
* The color of the highlight.
*
* @param blendMode
* The blend mode of the highlight.
*/
@Immutable
data class Soft(
override val width: Dp = 1f.dp,
override val color: Color = Color.White.copy(alpha = 0.4f),
override val blendMode: BlendMode = BlendMode.Plus
) : GlassHighlight {
override fun createRenderEffect(size: Size, density: Density, cornerRadius: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = with(density) { (width / 2f).toPx() }
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
} else {
null
}
}
}
/**
* Dynamic highlight effect with shimmering effect.
*
@@ -90,54 +127,63 @@ sealed interface GlassHighlight {
override val color: Color = Color.White.copy(alpha = 0.4f),
override val blendMode: BlendMode = BlendMode.Plus,
val angle: Float = 45f,
@param:FloatRange(from = 0.0) val falloff: Float = 1.5f
@param:FloatRange(from = 0.0) val falloff: Float = 1f
) : GlassHighlight {
private var highlightShaderCache: RuntimeShader? = null
private var highlightShader: RuntimeShader? = null
override fun createRenderEffect(size: Size, density: Density, cornerRadius: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = with(density) { (width / 2f).toPx() }
val blurRenderEffect =
cachedBlurRenderEffect
?: RenderEffect.createBlurEffect(
with(density) { 0.5f.dp.toPx() },
with(density) { 0.5f.dp.toPx() },
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
).also { cachedBlurRenderEffect = it }
)
} else {
null
}
val highlightShader = highlightShaderCache
val shader = highlightShader
?: RuntimeShader(GlassShaders.highlightShaderString)
.also { highlightShaderCache = it }
.also { highlightShader = it }
val highlightRenderEffect =
RenderEffect.createRuntimeShaderEffect(
highlightShader.apply {
shader.apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadius", cornerRadius)
setFloatUniform("angle", angle * (PI / 180f).toFloat())
setFloatUniform("falloff", falloff)
},
"image"
)
RenderEffect.createChainEffect(
highlightRenderEffect,
blurRenderEffect
)
if (blurRenderEffect != null) {
RenderEffect.createChainEffect(
highlightRenderEffect,
blurRenderEffect
)
} else {
highlightRenderEffect
}
} else {
null
}
}
private companion object {
var cachedBlurRenderEffect: RenderEffect? = null
}
}
companion object {
@Stable
val Default: Dynamic = Dynamic()
@Stable
val Solid: Solid = Solid()
@Stable
val Soft: Soft = Soft()
}
}
@@ -131,7 +131,7 @@ half4 main(float2 coord) {
uniform shader image;
uniform float2 size;
uniform float cornerRadius;
uniform float angle;
uniform float falloff;
@@ -141,11 +141,9 @@ half4 main(float2 coord) {
float2 halfSize = size * 0.5;
float2 centeredCoord = coord - halfSize;
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, cornerRadius);
float2 topLightNormal = float2(-cos(angle), -sin(angle));
float topLightFraction = dot(topLightNormal, grad);
float bottomLightFraction = -topLightFraction;
float fraction = pow(max(topLightFraction, bottomLightFraction), falloff);
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, min(halfSize.x, halfSize.y));
float2 normal = float2(-cos(angle), -sin(angle));
float fraction = pow(abs(dot(normal, grad)), falloff);
return image.eval(coord) * fraction;
}"""