Skip to content

Commit d346a06

Browse files
authored
Added a "solar flare" shader and modifier (android#1453)
The Kotlin side of this is very similar to the existing yellow striped modifier, but the shader is entirely different. Right now this isn't used anywhere but can be tested by swapping the yellowBackground with solarFlareShaderBackground.
2 parents 8656c87 + 05bd1b6 commit d346a06

File tree

4 files changed

+173
-24
lines changed

4 files changed

+173
-24
lines changed

JetLagged/app/src/main/java/com/example/jetlagged/JetLaggedScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import androidx.compose.ui.graphics.Color
4040
import androidx.compose.ui.unit.dp
4141
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4242
import androidx.lifecycle.viewmodel.compose.viewModel
43-
import com.example.jetlagged.backgrounds.yellowBackground
43+
import com.example.jetlagged.backgrounds.movingStripesBackground
4444
import com.example.jetlagged.data.JetLaggedHomeScreenViewModel
4545
import com.example.jetlagged.heartrate.HeartRateCard
4646
import com.example.jetlagged.sleep.JetLaggedHeader
@@ -63,7 +63,7 @@ fun JetLaggedScreen(
6363
.verticalScroll(rememberScrollState())
6464
.background(Color.White)
6565
) {
66-
Column(modifier = Modifier.yellowBackground()) {
66+
Column(modifier = Modifier.movingStripesBackground()) {
6767
JetLaggedHeader(
6868
modifier = Modifier.fillMaxWidth(),
6969
onDrawerClicked = onDrawerClicked
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.jetlagged.backgrounds
18+
19+
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.draw.drawWithCache
21+
import androidx.compose.ui.graphics.Brush
22+
import com.example.jetlagged.ui.theme.White
23+
import com.example.jetlagged.ui.theme.Yellow
24+
import com.example.jetlagged.ui.theme.YellowVariant
25+
26+
fun Modifier.simpleGradient(): Modifier =
27+
drawWithCache {
28+
val gradientBrush = Brush.verticalGradient(listOf(Yellow, YellowVariant, White))
29+
onDrawBehind {
30+
drawRect(gradientBrush, alpha = 1f)
31+
}
32+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.jetlagged.backgrounds
18+
19+
import android.graphics.RuntimeShader
20+
import android.os.Build
21+
import androidx.annotation.RequiresApi
22+
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
23+
import androidx.compose.runtime.mutableFloatStateOf
24+
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.graphics.ShaderBrush
26+
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
27+
import androidx.compose.ui.node.DrawModifierNode
28+
import androidx.compose.ui.node.ModifierNodeElement
29+
import com.example.jetlagged.ui.theme.Yellow
30+
import kotlinx.coroutines.launch
31+
import org.intellij.lang.annotations.Language
32+
33+
/**
34+
* Background modifier that displays a custom shader for Android T and above and a linear gradient
35+
* for older versions of Android
36+
*/
37+
fun Modifier.solarFlareShaderBackground(): Modifier =
38+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
39+
this.then(SolarFlareShaderBackgroundElement)
40+
} else {
41+
this.then(Modifier.simpleGradient())
42+
}
43+
44+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
45+
private data object SolarFlareShaderBackgroundElement :
46+
ModifierNodeElement<SolarFlairShaderBackgroundNode>() {
47+
override fun create() = SolarFlairShaderBackgroundNode()
48+
override fun update(node: SolarFlairShaderBackgroundNode) {
49+
}
50+
}
51+
52+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
53+
private class SolarFlairShaderBackgroundNode : DrawModifierNode, Modifier.Node() {
54+
private val shader = RuntimeShader(SHADER)
55+
private val shaderBrush = ShaderBrush(shader)
56+
private val time = mutableFloatStateOf(0f)
57+
58+
init {
59+
shader.setColorUniform(
60+
"baseColor",
61+
android.graphics.Color.valueOf(Yellow.red, Yellow.green, Yellow.blue, Yellow.alpha)
62+
)
63+
}
64+
65+
override fun ContentDrawScope.draw() {
66+
shader.setFloatUniform("resolution", size.width, size.height)
67+
shader.setFloatUniform("time", time.floatValue)
68+
69+
drawRect(shaderBrush)
70+
drawContent()
71+
}
72+
73+
override fun onAttach() {
74+
coroutineScope.launch {
75+
while (isAttached) {
76+
withInfiniteAnimationFrameMillis {
77+
time.floatValue = it / 1000f
78+
}
79+
}
80+
}
81+
}
82+
}
83+
84+
@Language("AGSL")
85+
private val SHADER = """
86+
uniform float2 resolution;
87+
uniform float time;
88+
layout(color) uniform half4 baseColor;
89+
90+
const int ITERATIONS = 2;
91+
const float INTENSITY = 100.0;
92+
const float TIME_MULTIPLIER = 0.25;
93+
94+
float4 main(in float2 fragCoord) {
95+
// Slow down the animation to be more soothing
96+
float calculatedTime = time * TIME_MULTIPLIER;
97+
98+
// Coords
99+
float2 uv = fragCoord / resolution.xy;
100+
float2 uvCalc = (uv * 5.0) - (INTENSITY * 2.0);
101+
102+
// Values to adjust per iteration
103+
float2 iterationChange = float2(uvCalc);
104+
float colorPart = 1.0;
105+
106+
for (int i = 0; i < ITERATIONS; i++) {
107+
iterationChange = uvCalc + float2(
108+
cos(calculatedTime + iterationChange.x) +
109+
sin(calculatedTime - iterationChange.y),
110+
cos(calculatedTime - iterationChange.x) +
111+
sin(calculatedTime + iterationChange.y)
112+
);
113+
colorPart += 0.8 / length(
114+
float2(uvCalc.x / (cos(iterationChange.x + calculatedTime) * INTENSITY),
115+
uvCalc.y / (sin(iterationChange.y + calculatedTime) * INTENSITY)
116+
)
117+
);
118+
}
119+
colorPart = 1.6 - (colorPart / float(ITERATIONS));
120+
121+
// Fade out the bottom on a curve
122+
float alpha = 1.0 - (uv.y * uv.y);
123+
// Mix calculated color with the incoming base color
124+
float4 color = float4(colorPart * baseColor.r, colorPart * baseColor.g, colorPart * baseColor.b, alpha);
125+
// Keep all channels within valid bounds of 0.0 and 1.0
126+
return clamp(color, 0.0, 1.0);
127+
}
128+
""".trimIndent()

JetLagged/app/src/main/java/com/example/jetlagged/backgrounds/ShaderBackground.kt renamed to JetLagged/app/src/main/java/com/example/jetlagged/backgrounds/StripesShaderBackground.kt

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,24 @@ import androidx.annotation.RequiresApi
2323
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
2424
import androidx.compose.runtime.mutableFloatStateOf
2525
import androidx.compose.ui.Modifier
26-
import androidx.compose.ui.draw.drawWithCache
27-
import androidx.compose.ui.graphics.Brush
2826
import androidx.compose.ui.graphics.ShaderBrush
2927
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
3028
import androidx.compose.ui.node.DrawModifierNode
3129
import androidx.compose.ui.node.ModifierNodeElement
32-
import com.example.jetlagged.ui.theme.White
3330
import com.example.jetlagged.ui.theme.Yellow
34-
import com.example.jetlagged.ui.theme.YellowVariant
3531
import kotlinx.coroutines.launch
3632
import org.intellij.lang.annotations.Language
3733

38-
private data object YellowBackgroundElement : ModifierNodeElement<YellowBackgroundNode>() {
34+
private data object MovingStripesBackgroundElement :
35+
ModifierNodeElement<MovingStripesBackgroundNode>() {
3936
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
40-
override fun create() = YellowBackgroundNode()
41-
override fun update(node: YellowBackgroundNode) {
37+
override fun create() = MovingStripesBackgroundNode()
38+
override fun update(node: MovingStripesBackgroundNode) {
4239
}
4340
}
4441

4542
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
46-
private class YellowBackgroundNode : DrawModifierNode, Modifier.Node() {
43+
private class MovingStripesBackgroundNode : DrawModifierNode, Modifier.Node() {
4744

4845
private val shader = RuntimeShader(SHADER)
4946
private val shaderBrush = ShaderBrush(shader)
@@ -59,7 +56,6 @@ private class YellowBackgroundNode : DrawModifierNode, Modifier.Node() {
5956
override fun ContentDrawScope.draw() {
6057
shader.setFloatUniform("resolution", size.width, size.height)
6158
shader.setFloatUniform("time", time.floatValue)
62-
shader.setFloatUniform("waves", 7f)
6359

6460
drawRect(shaderBrush)
6561

@@ -77,23 +73,17 @@ private class YellowBackgroundNode : DrawModifierNode, Modifier.Node() {
7773
}
7874
}
7975

80-
fun Modifier.yellowBackground(): Modifier =
76+
fun Modifier.movingStripesBackground(): Modifier =
8177
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
82-
this.then(YellowBackgroundElement)
78+
this.then(MovingStripesBackgroundElement)
8379
} else {
84-
drawWithCache {
85-
val gradientBrush = Brush.verticalGradient(listOf(Yellow, YellowVariant, White))
86-
onDrawBehind {
87-
drawRect(gradientBrush, alpha = 1f)
88-
}
89-
}
80+
this.then(Modifier.simpleGradient())
9081
}
9182

9283
@Language("AGSL")
93-
val SHADER = """
84+
private val SHADER = """
9485
uniform float2 resolution;
9586
uniform float time;
96-
uniform float waves;
9787
layout(color) uniform half4 color;
9888
9989
float calculateColorMultiplier(float yCoord, float factor) {
@@ -104,7 +94,7 @@ val SHADER = """
10494
// Config values
10595
const float speedMultiplier = 1.5;
10696
const float waveDensity = 1.0;
107-
//const float loops = iWaves;
97+
const float waves = 7.0;
10898
const float energy = 0.6;
10999
110100
// Calculated values
@@ -114,8 +104,7 @@ val SHADER = """
114104
float hAdjustment = uv.x * 4.3;
115105
float3 loopColor = vec3(1.0 - rgbColor.r, 1.0 - rgbColor.g, 1.0 - rgbColor.b) / waves;
116106
117-
for (float i = 1.0; i <= 100; i += 1.0) {
118-
if (i > waves) break;
107+
for (float i = 1.0; i <= waves; i += 1.0) {
119108
float loopFactor = i * 0.1;
120109
float sinInput = (timeOffset + hAdjustment) * energy;
121110
float curve = sin(sinInput) * (1.0 - loopFactor) * 0.05;

0 commit comments

Comments
 (0)