Add Inverse luminosity in TurbulenceNoiseShader.
Bug: 271134655 Test: Manual, atest TurbulenceNoiseShaderTest, TurbulenceNoiseViewTest Change-Id: Ibfc6f33a451e67ae4fe7e0e2f573e54df5a8bc63
This commit is contained in:
@@ -15,163 +15,166 @@
|
||||
*/
|
||||
package com.android.systemui.surfaceeffects.shaderutil
|
||||
|
||||
/** A common utility functions that are used for computing shaders. */
|
||||
class ShaderUtilLibrary {
|
||||
/** Common utility functions that are used for computing shaders. */
|
||||
object ShaderUtilLibrary {
|
||||
// language=AGSL
|
||||
companion object {
|
||||
const val SHADER_LIB =
|
||||
"""
|
||||
float triangleNoise(vec2 n) {
|
||||
n = fract(n * vec2(5.3987, 5.4421));
|
||||
n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
|
||||
float xy = n.x * n.y;
|
||||
// compute in [0..2[ and remap to [-1.0..1.0[
|
||||
return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
|
||||
const val SHADER_LIB =
|
||||
"""
|
||||
float triangleNoise(vec2 n) {
|
||||
n = fract(n * vec2(5.3987, 5.4421));
|
||||
n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
|
||||
float xy = n.x * n.y;
|
||||
// compute in [0..2[ and remap to [-1.0..1.0[
|
||||
return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
|
||||
}
|
||||
|
||||
const float PI = 3.1415926535897932384626;
|
||||
|
||||
float sparkles(vec2 uv, float t) {
|
||||
float n = triangleNoise(uv);
|
||||
float s = 0.0;
|
||||
for (float i = 0; i < 4; i += 1) {
|
||||
float l = i * 0.01;
|
||||
float h = l + 0.1;
|
||||
float o = smoothstep(n - l, h, n);
|
||||
o *= abs(sin(PI * o * (t + 0.55 * i)));
|
||||
s += o;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const float PI = 3.1415926535897932384626;
|
||||
vec2 distort(vec2 p, float time, float distort_amount_radial,
|
||||
float distort_amount_xy) {
|
||||
float angle = atan(p.y, p.x);
|
||||
return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
|
||||
cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
|
||||
+ vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
|
||||
cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
|
||||
}
|
||||
|
||||
float sparkles(vec2 uv, float t) {
|
||||
float n = triangleNoise(uv);
|
||||
float s = 0.0;
|
||||
for (float i = 0; i < 4; i += 1) {
|
||||
float l = i * 0.01;
|
||||
float h = l + 0.1;
|
||||
float o = smoothstep(n - l, h, n);
|
||||
o *= abs(sin(PI * o * (t + 0.55 * i)));
|
||||
s += o;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
// Perceived luminosity (L′), not absolute luminosity.
|
||||
half getLuminosity(vec3 c) {
|
||||
return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
|
||||
}
|
||||
|
||||
vec2 distort(vec2 p, float time, float distort_amount_radial,
|
||||
float distort_amount_xy) {
|
||||
float angle = atan(p.y, p.x);
|
||||
return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
|
||||
cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
|
||||
+ vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
|
||||
cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
|
||||
}
|
||||
// Creates a luminosity mask and clamp to the legal range.
|
||||
vec3 maskLuminosity(vec3 dest, float lum) {
|
||||
dest.rgb *= vec3(lum);
|
||||
// Clip back into the legal range
|
||||
dest = clamp(dest, vec3(0.), vec3(1.0));
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Perceived luminosity (L′), not absolute luminosity.
|
||||
half getLuminosity(vec3 c) {
|
||||
return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
|
||||
}
|
||||
// Return range [-1, 1].
|
||||
vec3 hash(vec3 p) {
|
||||
p = fract(p * vec3(.3456, .1234, .9876));
|
||||
p += dot(p, p.yxz + 43.21);
|
||||
p = (p.xxy + p.yxx) * p.zyx;
|
||||
return (fract(sin(p) * 4567.1234567) - .5) * 2.;
|
||||
}
|
||||
|
||||
// Creates a luminosity mask and clamp to the legal range.
|
||||
vec3 maskLuminosity(vec3 dest, float lum) {
|
||||
dest.rgb *= vec3(lum);
|
||||
// Clip back into the legal range
|
||||
dest = clamp(dest, vec3(0.), vec3(1.0));
|
||||
return dest;
|
||||
}
|
||||
// Skew factors (non-uniform).
|
||||
const half SKEW = 0.3333333; // 1/3
|
||||
const half UNSKEW = 0.1666667; // 1/6
|
||||
|
||||
// Return range [-1, 1].
|
||||
vec3 hash(vec3 p) {
|
||||
p = fract(p * vec3(.3456, .1234, .9876));
|
||||
p += dot(p, p.yxz + 43.21);
|
||||
p = (p.xxy + p.yxx) * p.zyx;
|
||||
return (fract(sin(p) * 4567.1234567) - .5) * 2.;
|
||||
}
|
||||
// Return range roughly [-1,1].
|
||||
// It's because the hash function (that returns a random gradient vector) returns
|
||||
// different magnitude of vectors. Noise doesn't have to be in the precise range thus
|
||||
// skipped normalize.
|
||||
half simplex3d(vec3 p) {
|
||||
// Skew the input coordinate, so that we get squashed cubical grid
|
||||
vec3 s = floor(p + (p.x + p.y + p.z) * SKEW);
|
||||
|
||||
// Skew factors (non-uniform).
|
||||
const half SKEW = 0.3333333; // 1/3
|
||||
const half UNSKEW = 0.1666667; // 1/6
|
||||
// Unskew back
|
||||
vec3 u = s - (s.x + s.y + s.z) * UNSKEW;
|
||||
|
||||
// Return range roughly [-1,1].
|
||||
// It's because the hash function (that returns a random gradient vector) returns
|
||||
// different magnitude of vectors. Noise doesn't have to be in the precise range thus
|
||||
// skipped normalize.
|
||||
half simplex3d(vec3 p) {
|
||||
// Skew the input coordinate, so that we get squashed cubical grid
|
||||
vec3 s = floor(p + (p.x + p.y + p.z) * SKEW);
|
||||
// Unskewed coordinate that is relative to p, to compute the noise contribution
|
||||
// based on the distance.
|
||||
vec3 c0 = p - u;
|
||||
|
||||
// Unskew back
|
||||
vec3 u = s - (s.x + s.y + s.z) * UNSKEW;
|
||||
// We have six simplices (in this case tetrahedron, since we are in 3D) that we
|
||||
// could possibly in.
|
||||
// Here, we are finding the correct tetrahedron (simplex shape), and traverse its
|
||||
// four vertices (c0..3) when computing noise contribution.
|
||||
// The way we find them is by comparing c0's x,y,z values.
|
||||
// For example in 2D, we can find the triangle (simplex shape in 2D) that we are in
|
||||
// by comparing x and y values. i.e. x>y lower, x<y, upper triangle.
|
||||
// Same applies in 3D.
|
||||
//
|
||||
// Below indicates the offsets (or offset directions) when c0=(x0,y0,z0)
|
||||
// x0>y0>z0: (1,0,0), (1,1,0), (1,1,1)
|
||||
// x0>z0>y0: (1,0,0), (1,0,1), (1,1,1)
|
||||
// z0>x0>y0: (0,0,1), (1,0,1), (1,1,1)
|
||||
// z0>y0>x0: (0,0,1), (0,1,1), (1,1,1)
|
||||
// y0>z0>x0: (0,1,0), (0,1,1), (1,1,1)
|
||||
// y0>x0>z0: (0,1,0), (1,1,0), (1,1,1)
|
||||
//
|
||||
// The rule is:
|
||||
// * For offset1, set 1 at the max component, otherwise 0.
|
||||
// * For offset2, set 0 at the min component, otherwise 1.
|
||||
// * For offset3, set 1 for all.
|
||||
//
|
||||
// Encode x0-y0, y0-z0, z0-x0 in a vec3
|
||||
vec3 en = c0 - c0.yzx;
|
||||
// Each represents whether x0>y0, y0>z0, z0>x0
|
||||
en = step(vec3(0.), en);
|
||||
// en.zxy encodes z0>x0, x0>y0, y0>x0
|
||||
vec3 offset1 = en * (1. - en.zxy); // find max
|
||||
vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min)
|
||||
vec3 offset3 = vec3(1.);
|
||||
|
||||
// Unskewed coordinate that is relative to p, to compute the noise contribution
|
||||
// based on the distance.
|
||||
vec3 c0 = p - u;
|
||||
vec3 c1 = c0 - offset1 + UNSKEW;
|
||||
vec3 c2 = c0 - offset2 + UNSKEW * 2.;
|
||||
vec3 c3 = c0 - offset3 + UNSKEW * 3.;
|
||||
|
||||
// We have six simplices (in this case tetrahedron, since we are in 3D) that we
|
||||
// could possibly in.
|
||||
// Here, we are finding the correct tetrahedron (simplex shape), and traverse its
|
||||
// four vertices (c0..3) when computing noise contribution.
|
||||
// The way we find them is by comparing c0's x,y,z values.
|
||||
// For example in 2D, we can find the triangle (simplex shape in 2D) that we are in
|
||||
// by comparing x and y values. i.e. x>y lower, x<y, upper triangle.
|
||||
// Same applies in 3D.
|
||||
//
|
||||
// Below indicates the offsets (or offset directions) when c0=(x0,y0,z0)
|
||||
// x0>y0>z0: (1,0,0), (1,1,0), (1,1,1)
|
||||
// x0>z0>y0: (1,0,0), (1,0,1), (1,1,1)
|
||||
// z0>x0>y0: (0,0,1), (1,0,1), (1,1,1)
|
||||
// z0>y0>x0: (0,0,1), (0,1,1), (1,1,1)
|
||||
// y0>z0>x0: (0,1,0), (0,1,1), (1,1,1)
|
||||
// y0>x0>z0: (0,1,0), (1,1,0), (1,1,1)
|
||||
//
|
||||
// The rule is:
|
||||
// * For offset1, set 1 at the max component, otherwise 0.
|
||||
// * For offset2, set 0 at the min component, otherwise 1.
|
||||
// * For offset3, set 1 for all.
|
||||
//
|
||||
// Encode x0-y0, y0-z0, z0-x0 in a vec3
|
||||
vec3 en = c0 - c0.yzx;
|
||||
// Each represents whether x0>y0, y0>z0, z0>x0
|
||||
en = step(vec3(0.), en);
|
||||
// en.zxy encodes z0>x0, x0>y0, y0>x0
|
||||
vec3 offset1 = en * (1. - en.zxy); // find max
|
||||
vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min)
|
||||
vec3 offset3 = vec3(1.);
|
||||
// Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution)
|
||||
//
|
||||
// First compute d^2, squared distance to the point.
|
||||
vec4 w; // w = max(0, r^2 - d^2))
|
||||
w.x = dot(c0, c0);
|
||||
w.y = dot(c1, c1);
|
||||
w.z = dot(c2, c2);
|
||||
w.w = dot(c3, c3);
|
||||
|
||||
vec3 c1 = c0 - offset1 + UNSKEW;
|
||||
vec3 c2 = c0 - offset2 + UNSKEW * 2.;
|
||||
vec3 c3 = c0 - offset3 + UNSKEW * 3.;
|
||||
// Noise contribution should decay to zero before they cross the simplex boundary.
|
||||
// Usually r^2 is 0.5 or 0.6;
|
||||
// 0.5 ensures continuity but 0.6 increases the visual quality for the application
|
||||
// where discontinuity isn't noticeable.
|
||||
w = max(0.6 - w, 0.);
|
||||
|
||||
// Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution)
|
||||
//
|
||||
// First compute d^2, squared distance to the point.
|
||||
vec4 w; // w = max(0, r^2 - d^2))
|
||||
w.x = dot(c0, c0);
|
||||
w.y = dot(c1, c1);
|
||||
w.z = dot(c2, c2);
|
||||
w.w = dot(c3, c3);
|
||||
// Noise contribution from each point.
|
||||
vec4 nc;
|
||||
nc.x = dot(hash(s), c0);
|
||||
nc.y = dot(hash(s + offset1), c1);
|
||||
nc.z = dot(hash(s + offset2), c2);
|
||||
nc.w = dot(hash(s + offset3), c3);
|
||||
|
||||
// Noise contribution should decay to zero before they cross the simplex boundary.
|
||||
// Usually r^2 is 0.5 or 0.6;
|
||||
// 0.5 ensures continuity but 0.6 increases the visual quality for the application
|
||||
// where discontinuity isn't noticeable.
|
||||
w = max(0.6 - w, 0.);
|
||||
nc *= w*w*w*w;
|
||||
|
||||
// Noise contribution from each point.
|
||||
vec4 nc;
|
||||
nc.x = dot(hash(s), c0);
|
||||
nc.y = dot(hash(s + offset1), c1);
|
||||
nc.z = dot(hash(s + offset2), c2);
|
||||
nc.w = dot(hash(s + offset3), c3);
|
||||
// Add all the noise contributions.
|
||||
// Should multiply by the possible max contribution to adjust the range in [-1,1].
|
||||
return dot(vec4(32.), nc);
|
||||
}
|
||||
|
||||
nc *= w*w*w*w;
|
||||
// Random rotations.
|
||||
// The way you create fractal noise is layering simplex noise with some rotation.
|
||||
// To make random cloud looking noise, the rotations should not align. (Otherwise it
|
||||
// creates patterned noise).
|
||||
// Below rotations only rotate in one axis.
|
||||
const mat3 rot1 = mat3(1.0, 0. ,0., 0., 0.15, -0.98, 0., 0.98, 0.15);
|
||||
const mat3 rot2 = mat3(-0.95, 0. ,-0.3, 0., 1., 0., 0.3, 0., -0.95);
|
||||
const mat3 rot3 = mat3(1.0, 0. ,0., 0., -0.44, -0.89, 0., 0.89, -0.44);
|
||||
|
||||
// Add all the noise contributions.
|
||||
// Should multiply by the possible max contribution to adjust the range in [-1,1].
|
||||
return dot(vec4(32.), nc);
|
||||
}
|
||||
// Octave = 4
|
||||
// Divide each coefficient by 3 to produce more grainy noise.
|
||||
half simplex3d_fractal(vec3 p) {
|
||||
return 0.675 * simplex3d(p * rot1) + 0.225 * simplex3d(2.0 * p * rot2)
|
||||
+ 0.075 * simplex3d(4.0 * p * rot3) + 0.025 * simplex3d(8.0 * p);
|
||||
}
|
||||
|
||||
// Random rotations.
|
||||
// The way you create fractal noise is layering simplex noise with some rotation.
|
||||
// To make random cloud looking noise, the rotations should not align. (Otherwise it
|
||||
// creates patterned noise).
|
||||
// Below rotations only rotate in one axis.
|
||||
const mat3 rot1 = mat3(1.0, 0. ,0., 0., 0.15, -0.98, 0., 0.98, 0.15);
|
||||
const mat3 rot2 = mat3(-0.95, 0. ,-0.3, 0., 1., 0., 0.3, 0., -0.95);
|
||||
const mat3 rot3 = mat3(1.0, 0. ,0., 0., -0.44, -0.89, 0., 0.89, -0.44);
|
||||
|
||||
// Octave = 4
|
||||
// Divide each coefficient by 3 to produce more grainy noise.
|
||||
half simplex3d_fractal(vec3 mat) {
|
||||
return 0.675 * simplex3d(mat * rot1) + 0.225 * simplex3d(2.0 * mat * rot2)
|
||||
+ 0.075 * simplex3d(4.0 * mat * rot3) + 0.025 * simplex3d(8.0 * mat);
|
||||
}
|
||||
"""
|
||||
}
|
||||
// Screen blend
|
||||
vec3 screen(vec3 dest, vec3 src) {
|
||||
return dest + src - dest * src;
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) :
|
||||
uniform float in_aspectRatio;
|
||||
uniform float in_opacity;
|
||||
uniform float in_pixelDensity;
|
||||
uniform float in_inverseLuma;
|
||||
layout(color) uniform vec4 in_color;
|
||||
layout(color) uniform vec4 in_backgroundColor;
|
||||
"""
|
||||
@@ -47,7 +48,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) :
|
||||
uv.x *= in_aspectRatio;
|
||||
|
||||
vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
|
||||
float luma = simplex3d(noiseP) * in_opacity;
|
||||
float luma = abs(in_inverseLuma - simplex3d(noiseP)) * in_opacity;
|
||||
vec3 mask = maskLuminosity(in_color.rgb, luma);
|
||||
vec3 color = in_backgroundColor.rgb + mask * 0.6;
|
||||
|
||||
@@ -69,7 +70,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) :
|
||||
uv.x *= in_aspectRatio;
|
||||
|
||||
vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
|
||||
float luma = simplex3d_fractal(noiseP) * in_opacity;
|
||||
float luma = abs(in_inverseLuma - simplex3d_fractal(noiseP)) * in_opacity;
|
||||
vec3 mask = maskLuminosity(in_color.rgb, luma);
|
||||
vec3 color = in_backgroundColor.rgb + mask * 0.6;
|
||||
|
||||
@@ -123,6 +124,17 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) :
|
||||
setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to inverse the luminosity of the noise.
|
||||
*
|
||||
* By default noise will be used as a luma matte as is. This means that you will see color in
|
||||
* the brighter area. If you want to invert it, meaning blend color onto the darker side, set to
|
||||
* true.
|
||||
*/
|
||||
fun setInverseNoiseLuminosity(inverse: Boolean) {
|
||||
setFloatUniform("in_inverseLuma", if (inverse) 1f else 0f)
|
||||
}
|
||||
|
||||
/** Current noise movements in x, y, and z axes. */
|
||||
var noiseOffsetX: Float = 0f
|
||||
private set
|
||||
|
||||
Reference in New Issue
Block a user