Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c32cb42c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Rain effect optimization" into main

parents fcfc8436 621c3b11
Loading
Loading
Loading
Loading
+0 −105
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * 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.
 */

uniform shader texture;
uniform float time;
uniform float screenAspectRatio;
uniform float gridScale;
uniform float2 screenSize;
uniform half intensity;

#include "shaders/constants.agsl"
#include "shaders/utils.agsl"
#include "shaders/glass_rain.agsl"
#include "shaders/rain_constants.agsl"

vec4 main(float2 fragCoord) {
    // 0. Calculate UV and add a bit of noise so that the droplets are not perfect circles.
    float2 uv = vec2(valueNoise(fragCoord) * 0.015 - 0.0025) + fragCoord / screenSize;

    // 1. Generate small glass rain.
    GlassRain smallDrippingRain = generateGlassRain(
         uv,
         screenAspectRatio,
         time * 0.7,
         /* Grid size = */ vec2(5.0, 1.6) * gridScale,
         intensity * 0.6);
    float dropMask = smallDrippingRain.dropMask;
    float droppletsMask = smallDrippingRain.droppletsMask;
    float trailMask = smallDrippingRain.trailMask;
    vec2 dropUvMasked = smallDrippingRain.drop * dropMask;
    vec2 droppletsUvMasked = smallDrippingRain.dropplets * droppletsMask;

    // 2. Generate medium size glass rain.
    GlassRain medDrippingRain = generateGlassRain(
          uv,
          screenAspectRatio,
          time * 0.80,
          /* Grid size = */ vec2(6., 0.945) * gridScale,
          intensity * 0.6);

    // 3. Combine those two glass rains.
    dropMask = max(medDrippingRain.dropMask, dropMask);
    droppletsMask = max(medDrippingRain.droppletsMask, droppletsMask);
    trailMask = max(medDrippingRain.trailMask, trailMask);
    dropUvMasked = mix(dropUvMasked,
        medDrippingRain.drop * medDrippingRain.dropMask, medDrippingRain.dropMask);
    droppletsUvMasked = mix(droppletsUvMasked,
        medDrippingRain.dropplets * medDrippingRain.droppletsMask, medDrippingRain.droppletsMask);

    // 4. Add static rain droplets on the glass surface. (They stay in place and dissapate.)
    vec2 gridSize = vec2(12., 12.) * gridScale;
    // Aspect ratio impacts visible cells.
    gridSize.y /= screenAspectRatio;
    vec3 staticRain = generateStaticGlassRain(uv, time, intensity, gridSize);
    dropMask = max(dropMask, staticRain.z);
    dropUvMasked = mix(dropUvMasked, staticRain.xy * staticRain.z, staticRain.z);

    // 5. Distort uv for the rain drops and dropplets.
    float distortionDrop = -0.1;
    vec2 uvDiffractionOffsets =
        distortionDrop * dropUvMasked;
    vec2 s = screenSize;
    // Ensure the diffracted image in drops is not inverted.
    s.y *= -1;

    vec4 color = texture.eval(fragCoord);
    vec3 sampledColor = texture.eval(fragCoord + uvDiffractionOffsets * s).rgb;
    color.rgb = mix(color.rgb, sampledColor, max(dropMask, droppletsMask));

    // 6. Add color tint to the rain drops.
    color.rgb = mix(
        color.rgb,
        dropTint,
        dropTintIntensity * smoothstep(0.7, 1., max(dropMask, droppletsMask)));

    // 7. Add highlight to the drops.
    color.rgb = mix(
        color.rgb,
        highlightColor,
        highlightIntensity
            * smoothstep(0.05, 0.08, max(dropUvMasked * 1.7, droppletsUvMasked * 2.6)).x);

    // 8. Add shadows to the drops.
    color.rgb = mix(
        color.rgb,
        contactShadowColor,
        dropShadowIntensity *
            smoothstep(0.055, 0.1, max(length(dropUvMasked * 1.7),
                length(droppletsUvMasked * 1.9))));

    return color;
}
 No newline at end of file
+0 −2
Original line number Diff line number Diff line
@@ -41,8 +41,6 @@ Rain generateRain(
    in float rainIntensity
) {
    /* Grid. */
    // Number of rows and columns (each one is a cell, a drop).
    float cellAspectRatio = rainGridSize.x / rainGridSize.y;
    // Aspect ratio impacts visible cells.
    uv.y /= screenAspectRatio;
    // scale the UV to allocate number of rows and columns.
+83 −6
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ uniform mat3 transformMatrixWeather;
#include "shaders/rain_shower.agsl"
#include "shaders/rain_constants.agsl"
#include "shaders/rain_splash.agsl"
#include "shaders/glass_rain.agsl"

// Controls how visible the rain drops are.
const float rainVisibility = 0.4;
@@ -79,6 +80,7 @@ vec3 drawSplashes(vec2 uv, vec2 fragCoord, vec3 color) {
}

vec4 main(float2 fragCoord) {
    // 1. Generate rain shower.
    // Apply transform matrix to fragCoord
    float2 uvTexture = transformPoint(transformMatrixBitmap, fragCoord);
    // Calculate uv for snow based on transformed coordinates
@@ -96,7 +98,7 @@ vec4 main(float2 fragCoord) {
    float variation = wiggle(time - uv.y * 1.1, 0.10);
    vec2 uvRot = rotateAroundPoint(uv, vec2(0.5, -1.42), variation * PI / 9.);

    // 1. Generate a layer of rain behind the subject.
    // 1.1. Generate a layer of rain behind the subject.
    Rain rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -106,7 +108,7 @@ vec4 main(float2 fragCoord) {

    color.rgb = mix(color.rgb, highlightColor, rainVisibility * rain.dropMask);

    // 2. Generate mid layer of rain behind the subject.
    // 1.2. Generate mid layer of rain behind the subject.
    rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -114,16 +116,16 @@ vec4 main(float2 fragCoord) {
          /* Grid size = */ vec2(30.0, 4.0) * gridScale,
          intensity);

    // 3. Blend those layers.
    // 1.3. Blend those layers.
    color.rgb = mix(color.rgb, highlightColor, rainVisibility * rain.dropMask);

    // 4. Blend with the foreground. Any effect from here will be in front of the subject.
    // 1.4. Blend with the foreground. Any effect from here will be in front of the subject.
    color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a);

    // 5. Draw splashes
    // (1.5. Draw splashes)
    color.rgb = drawSplashes(uv, fragCoord, color.rgb);

    // 6. Generate a layer of rain in front of the subject (bigger and faster).
    // 1.6. Generate a layer of rain in front of the subject (bigger and faster).
    rain = generateRain(
          uvRot,
          screenAspectRatio,
@@ -134,5 +136,80 @@ vec4 main(float2 fragCoord) {
    // Closer rain drops are less visible.
    color.rgb = mix(color.rgb, highlightColor, 0.7 * rainVisibility * rain.dropMask);

    // 2. Generate glass rain layer.
    // 2.0. Calculate UV and add a bit of noise so that the droplets are not perfect circles.
    float2 glassUv = vec2(valueNoise(fragCoord) * 0.015 - 0.0025) + fragCoord / screenSize;

    // 2.1. Generate small glass rain.
    GlassRain smallDrippingRain = generateGlassRain(
         glassUv,
         screenAspectRatio,
         time * 0.7,
         /* Grid size = */ vec2(5.0, 1.6) * gridScale,
         intensity * 0.6);
    float dropMask = smallDrippingRain.dropMask;
    float droppletsMask = smallDrippingRain.droppletsMask;
    float trailMask = smallDrippingRain.trailMask;
    vec2 dropUvMasked = smallDrippingRain.drop * dropMask;
    vec2 droppletsUvMasked = smallDrippingRain.dropplets * droppletsMask;

    // 2.2. Generate medium size glass rain.
    GlassRain medDrippingRain = generateGlassRain(
          glassUv,
          screenAspectRatio,
          time * 0.80,
          /* Grid size = */ vec2(6., 0.945) * gridScale,
          intensity * 0.6);

    // 2.3. Combine those two glass rains.
    dropMask = max(medDrippingRain.dropMask, dropMask);
    droppletsMask = max(medDrippingRain.droppletsMask, droppletsMask);
    trailMask = max(medDrippingRain.trailMask, trailMask);
    dropUvMasked = mix(dropUvMasked,
        medDrippingRain.drop * medDrippingRain.dropMask, medDrippingRain.dropMask);
    droppletsUvMasked = mix(droppletsUvMasked,
        medDrippingRain.dropplets * medDrippingRain.droppletsMask, medDrippingRain.droppletsMask);

    // 2.4. Add static rain droplets on the glass surface. (They stay in place and dissapate.)
    vec2 gridSize = vec2(12., 12.) * gridScale;
    // Aspect ratio impacts visible cells.
    gridSize.y /= screenAspectRatio;
    vec3 staticRain = generateStaticGlassRain(glassUv, time, intensity, gridSize);
    dropMask = max(dropMask, staticRain.z);
    dropUvMasked = mix(dropUvMasked, staticRain.xy * staticRain.z, staticRain.z);

    // 2.5. Distort uv for the rain drops and dropplets.
    float distortionDrop = -0.1;
    vec2 uvDiffractionOffsets =
        distortionDrop * dropUvMasked;
     vec2  s = screenSize;
    // Ensure the diffracted image in drops is not inverted.
    s.y *= -1;

     vec3 sampledColor = background.eval(uvTexture + uvDiffractionOffsets * s).rgb;
    sampledColor = imageRangeConversion(sampledColor, 0.84, 0.02, noise, intensity);
    color.rgb = mix(color.rgb, sampledColor, max(dropMask, droppletsMask));

    // 2.6. Add color tint to the rain drops.
    color.rgb = mix(
        color.rgb,
        dropTint,
        dropTintIntensity * smoothstep(0.7, 1., max(dropMask, droppletsMask)));

    // 2.7. Add highlight to the drops.
    color.rgb = mix(
        color.rgb,
        highlightColor,
        highlightIntensity
            * smoothstep(0.05, 0.08, max(dropUvMasked * 1.7, droppletsUvMasked * 2.6)).x);

    // 2.8. Add shadows to the drops.
    color.rgb = mix(
        color.rgb,
        contactShadowColor,
        dropShadowIntensity *
            smoothstep(0.055, 0.1, max(length(dropUvMasked * 1.7),
                length(droppletsUvMasked * 1.9))));

    return color;
}
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.SizeF
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTransformDifference
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.centerCropMatrix
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScale
import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix
import kotlin.random.Random

@@ -47,6 +48,7 @@ abstract class WeatherEffectBase(
    // Apply to weather components not rely on image textures
    // Should be identity matrix in editor, and only change when parallax applied in homescreen
    private val transformMatrixWeather: FloatArray = FloatArray(9)
    protected var bitmapScale = getScale(centerCropMatrix)
    protected var elapsedTime: Float = 0f

    abstract val shader: RuntimeShader
@@ -56,6 +58,7 @@ abstract class WeatherEffectBase(

    override fun setMatrix(matrix: Matrix) {
        this.parallaxMatrix.set(matrix)
        bitmapScale = getScale(parallaxMatrix)
        adjustCropping(surfaceSize)
    }

@@ -111,6 +114,7 @@ abstract class WeatherEffectBase(
                SizeF(background.width.toFloat(), background.height.toFloat()),
            )
        parallaxMatrix.set(centerCropMatrix)
        bitmapScale = getScale(centerCropMatrix)
        shader.setInputBuffer(
            "background",
            BitmapShader(this.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
+24 −47
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.google.android.wallpaper.weathereffects.graphics.rain
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
@@ -29,7 +28,6 @@ import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY
import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase
import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
import com.google.android.wallpaper.weathereffects.graphics.utils.SolidColorShader
import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils
import java.util.concurrent.Executor

@@ -46,12 +44,17 @@ class RainEffect(
) : WeatherEffectBase(foreground, background, surfaceSize) {

    private val rainPaint = Paint().also { it.shader = rainConfig.colorGradingShader }

    // Set blur effect to reduce the outline noise. No need to set blur effect every time we
    // re-generate the outline buffer.
    // Outline buffer is set with bitmap size, so we need to multiply blur radius by scale to get
    // consistent blur across different surface
    private var outlineBuffer =
        FrameBuffer(background.width, background.height).apply {
            setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
            setRenderEffect(
                RenderEffect.createBlurEffect(
                    2f / bitmapScale,
                    2f / bitmapScale,
                    Shader.TileMode.CLAMP,
                )
            )
        }
    private val outlineBufferPaint = Paint().also { it.shader = rainConfig.outlineShader }

@@ -67,43 +70,19 @@ class RainEffect(
        elapsedTime += TimeUtils.millisToSeconds(deltaMillis)

        rainConfig.rainShowerShader.setFloatUniform("time", elapsedTime)
        rainConfig.glassRainShader.setFloatUniform("time", elapsedTime)

        rainConfig.glassRainShader.setInputShader("texture", rainConfig.rainShowerShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader)
    }

    override fun draw(canvas: Canvas) {
        canvas.drawPaint(rainPaint)
    }

    override fun release() {
        super.release()
        outlineBuffer.close()
    }

    override fun setIntensity(intensity: Float) {
        super.setIntensity(intensity)
        rainConfig.glassRainShader.setFloatUniform("intensity", intensity)
        val thickness = 1f + intensity * 10f
        rainConfig.outlineShader.setFloatUniform("thickness", thickness)

        // Need to recreate the outline buffer as the uniform has changed.
        createOutlineBuffer()
    }

    override fun setBitmaps(foreground: Bitmap?, background: Bitmap): Boolean {
        if (!super.setBitmaps(foreground, background)) {
            return false
        }
        outlineBuffer.close()
        outlineBuffer =
            FrameBuffer(background.width, background.height).apply {
                setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP))
            }
        updateTextureUniforms()

        // Need to recreate the outline buffer as the outlineBuffer has changed due to background
        updateTextureUniforms()
        createOutlineBuffer()
        return true
    }
@@ -120,18 +99,6 @@ class RainEffect(
    override val colorGradingIntensity: Float
        get() = rainConfig.colorGradingIntensity

    override fun adjustCropping(newSurfaceSize: SizeF) {
        super.adjustCropping(newSurfaceSize)
        rainConfig.glassRainShader.setFloatUniform(
            "screenSize",
            newSurfaceSize.width,
            newSurfaceSize.height,
        )

        val screenAspectRatio = GraphicsUtils.getAspectRatio(newSurfaceSize)
        rainConfig.glassRainShader.setFloatUniform("screenAspectRatio", screenAspectRatio)
    }

    override fun updateTextureUniforms() {
        val foregroundBuffer =
            BitmapShader(super.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR)
@@ -144,7 +111,15 @@ class RainEffect(
        )
    }

    /**
     * It's necessary to create outline buffer only when bitmaps change, only intensity change won't
     * create a new one cause we refer to intensity in each cell when drawing splashes.
     */
    private fun createOutlineBuffer() {
        rainConfig.outlineShader.setFloatUniform(
            "thickness",
            MAX_RAIN_OUTLINE_THICKNESS / bitmapScale,
        )
        val canvas = outlineBuffer.beginDrawing()
        canvas.drawPaint(outlineBufferPaint)
        outlineBuffer.endDrawing()
@@ -162,8 +137,7 @@ class RainEffect(

    private fun prepareColorGrading() {
        // Initialize the buffer with black, so that we don't ever draw garbage buffer.
        rainConfig.glassRainShader.setInputShader("texture", SolidColorShader(Color.BLACK))
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader)
        rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader)
        rainConfig.lut?.let {
            rainConfig.colorGradingShader.setInputShader(
                "lut",
@@ -176,6 +150,9 @@ class RainEffect(
        val widthScreenScale =
            GraphicsUtils.computeDefaultGridSize(newSurfaceSize, rainConfig.pixelDensity)
        rainConfig.rainShowerShader.setFloatUniform("gridScale", widthScreenScale)
        rainConfig.glassRainShader.setFloatUniform("gridScale", widthScreenScale)
    }

    companion object {
        const val MAX_RAIN_OUTLINE_THICKNESS = 11f
    }
}
Loading