吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2437|回复: 23
收起左侧

[其他原创] 安卓液态玻璃效果初步实现

  [复制链接]
当钟声敲响回忆 发表于 2025-6-18 20:28
本帖最后由 当钟声敲响回忆 于 2025-6-18 22:42 编辑

AGSL shader代码如下
[Asm] 纯文本查看 复制代码
uniform float3 iResolution; 
uniform float  iTime; 
uniform float4 iMouse; 
uniform float3 iImageResolution; 
uniform shader iImage1;
uniform float  useCircle; float2 R;
const float PI = 3.14159265;

// 创建旋转矩阵
float2x2 Rot(float a) {
    float c = cos(a);
    float s = sin(a);
    return float2x2(c, -s, s, c);
}

// 像素归一化处理
float PX(float a) {
    return a / R.y;
}

// 矩形距离场
float Box(float2 p, float2 b) {
    float2 d = abs(p) - b;
    return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}

// 圆形距离场
float Circle(float2 p, float r) {
    return length(p) - r;
}

// 根据 useCircle 选择形状(圆形或圆角矩形)
float Shape(float2 p, float2 b, float r) {
    return useCircle > 0.5 ? Circle(p, r) : Box(p, b);
}

// 液体玻璃效果
float4 LiquidGlass(float2 uv, float direction, float quality, float size) {
    float2 radius = size / R;
    float4 color = iImage1.eval(uv * iImageResolution.xy);

    float d_step = PI / direction; // 方向步长,direction = 10.0
    float i_step = 1.0 / quality;  // 质量步长,quality = 10.0
    float d = 0.0;
    for (int j = 0; j < 10; j++) { // 固定10次迭代(direction = 10.0)
        float i = i_step;
        for (int k = 0; k < 10; k++) { // 固定10次迭代(quality = 10.0)
            color += iImage1.eval((uv + float2(cos(d), sin(d)) * radius * i) * iImageResolution.xy);
            i += i_step;
        }
        d += d_step;
    }

    color /= quality * direction; // 归一化,10.0 * 10.0 = 100.0
    return color;
}

// 形状扭曲效果
float4 Distortion(float2 uv) {
    float shape = Shape(uv, float2(PX(50.0)), PX(50.0));
    float shapeShape = smoothstep(PX(1.5), 0.0, shape - PX(50.0)); // 形状平滑过渡
    float shapeDisp = smoothstep(PX(75.0), 0.0, shape - PX(25.0)); // 边框宽度
    float shapeLight = shapeShape * smoothstep(0.0, PX(20.0), shape - PX(40.0)); // 光照强度
    return float4(shapeShape, shapeDisp, shapeLight, 0.0);
}

float4 main(float2 I) {
    R = iResolution.xy;
    float2 uv = I / R; // 归一化UV坐标
    float2 st = (I - 0.5 * R) / R.y; // 屏幕空间坐标
    float2 M = iMouse.xy == float2(0.0) ? float2(0.0) : (iMouse.xy - 0.5 * R) / R.y; // 鼠标位置

    float4 dist = Distortion(st - M); // 计算扭曲效果

    float2 uv2 = uv - iMouse.xy / R; // 为了适配shaders.skia.org的调试
    uv2 *= 0.5 + 0.5 * smoothstep(0.5, 1.0, dist.y); // 缩放UV
    uv2 += iMouse.xy / R;

    float3 col = mix(float3(0.0), // 透明黑色背景
                     0.2 + LiquidGlass(uv2, 10.0, 10.0, 5.0).rgb * 0.7, // 应用液体玻璃效果
                     dist.x); // 根据图标形状混合
    col += dist.z * 0.9 + dist.w; // 添加图标光照和图案

    // 使用 dist.x 控制透明度:图标区域不透明,其他区域透明
    float alpha = dist.x > 0.0 ? 1.0 : 0.0;

    // 应用阴影效果,保持透明度
    col *= 1.0 - 0.2 * smoothstep(PX(80.0), 0.0, Shape(st - M + float2(0.0, PX(40.0)), float2(PX(50.0)), PX(50.0)));

    return float4(col, alpha); // 返回最终颜色和透明度
}

在kotlin中新声明一个继承于View的类,然后应用这个Shader即可
[Java] 纯文本查看 复制代码
init {
        val shaderCode = context.resources.openRawResource(R.raw.lens_shader)
            .bufferedReader().use { it.readText() }
        runtimeShader = RuntimeShader(shaderCode)
    }

效果如下:

免费评分

参与人数 3威望 +1 吾爱币 +12 热心值 +3 收起 理由
苏紫方璇 + 1 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
zz2256891 + 1 + 1 我很赞同!
FYL11162022 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

52weiv 发表于 2025-6-19 09:32
非常强, 我让ai将你的代码转成了html的代码。居然也能看到效果。
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GLSL 液体玻璃效果演示</title>
    <style>
        /* CSS 样式,用于让画布铺满整个窗口 */
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
        }
        canvas {
            display: block;
            width: 100vw;
            height: 100vh;
        }
        /* 控制面板的样式 */
        #controls {
            position: absolute;
            top: 10px;
            left: 10px;
            padding: 10px;
            background-color: rgba(0, 0, 0, 0.5);
            color: white;
            font-family: sans-serif;
            border-radius: 5px;
            user-select: none;
        }
        #controls label {
            cursor: pointer;
        }
    </style>
</head>
<body>
    <canvas id="glslCanvas"></canvas>

    <img id="backgroundImage" src="https://pic2.zhimg.com/v2-a53c740141ab75eb4fe16af3ef8c35c5_r.jpg" crossorigin="anonymous" style="display:none;">

    <div id="controls">
        <label>
            <input type="checkbox" id="useCircleCheckbox">
            使用圆形
        </label>
    </div>

    <script id="vertex-shader" type="x-shader/x-vertex">
        // 这个顶点着色器非常基础,它只是将顶点位置传递出去
        // 这样我们的片段着色器就可以在构成平面的两个三角形上运行
        attribute vec2 a_position;
        void main() {
            gl_Position = vec4(a_position, 0.0, 1.0);
        }
    </script>

    <script id="fragment-shader" type="x-shader/x-fragment">
        precision mediump float; // 为浮点数设置中等精度

        // 从 JavaScript 传入的变量
        uniform vec3 iResolution;
        uniform float iTime;
        uniform vec4 iMouse;
        uniform vec3 iImageResolution;
        uniform sampler2D iImage1;
        uniform float useCircle;

        // --- 以下是您提供的 GLSL 代码,已适配 WebGL ---
        vec2 R;
        const float PI = 3.14159265;

        // 创建旋转矩阵
        mat2 Rot(float a) {
            float c = cos(a);
            float s = sin(a);
            return mat2(c, -s, s, c);
        }

        // 像素归一化处理
        float PX(float a) {
            return a / R.y;
        }

        // 矩形距离场
        float Box(vec2 p, vec2 b) {
            vec2 d = abs(p) - b;
            return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
        }

        // 圆形距离场
        float Circle(vec2 p, float r) {
            return length(p) - r;
        }

        // 根据 useCircle 选择形状(圆形或圆角矩形)
        float Shape(vec2 p, vec2 b, float r) {
            return useCircle > 0.5 ? Circle(p, r) : Box(p, b);
        }

        // 液体玻璃效果
        vec4 LiquidGlass(vec2 uv, float direction, float quality, float size) {
            vec2 radius = size / R;
            vec4 color = texture2D(iImage1, uv);

            float d_step = PI / direction; // 方向步长
            float i_step = 1.0 / quality;  // 质量步长
            float d = 0.0;
            // 循环被展开以兼容一些旧的GPU
            for (int j = 0; j < 10; j++) {
                if (float(j) >= direction) break;
                float i = i_step;
                for (int k = 0; k < 10; k++) {
                    if (float(k) >= quality) break;
                    color += texture2D(iImage1, uv + vec2(cos(d), sin(d)) * radius * i);
                    i += i_step;
                }
                d += d_step;
            }

            color /= quality * direction + 1.0; // 归一化
            return color;
        }

        // 形状扭曲效果
        vec4 Distortion(vec2 uv) {
            float shape = Shape(uv, vec2(PX(50.0)), PX(50.0));
            float shapeShape = smoothstep(PX(1.5), 0.0, shape - PX(50.0)); // 形状平滑过渡
            float shapeDisp = smoothstep(PX(75.0), 0.0, shape - PX(25.0)); // 边框宽度
            float shapeLight = shapeShape * smoothstep(0.0, PX(20.0), shape - PX(40.0)); // 光照强度
            return vec4(shapeShape, shapeDisp, shapeLight, 0.0);
        }

        void main() {
            R = iResolution.xy;
            vec2 uv = gl_FragCoord.xy / R; // 归一化UV坐标
            vec2 st = (gl_FragCoord.xy - 0.5 * R) / R.y; // 屏幕空间坐标
            vec2 M = iMouse.xy == vec2(0.0) ? vec2(0.0) : (iMouse.xy - 0.5 * R) / R.y; // 鼠标位置

            // 如果鼠标没有移动过,则将效果定位在中心
            if (iMouse.x == 0.0 && iMouse.y == 0.0) {
                 M = vec2(0.0);
            }

            vec4 dist = Distortion(st - M); // 计算扭曲效果

            vec2 uv2 = uv;
            uv2 *= 0.5 + 0.5 * smoothstep(0.5, 1.0, dist.y); // 缩放UV

            vec3 col = mix(vec3(0.0), // 透明黑色背景
                           0.2 + LiquidGlass(uv2, 10.0, 10.0, 5.0).rgb * 0.7, // 应用液体玻璃效果
                           dist.x); // 根据图标形状混合
            col += dist.z * 0.9 + dist.w; // 添加图标光照和图案

            // 应用阴影效果
            col *= 1.0 - 0.2 * smoothstep(PX(80.0), 0.0, Shape(st - M + vec2(0.0, PX(40.0)), vec2(PX(50.0)), PX(50.0)));

            // 使用 dist.x 控制透明度:图标区域不透明,其他区域透明
            float alpha = dist.x;

            gl_FragColor = vec4(col, alpha); // 返回最终颜色和透明度
        }
    </script>

    <script>
        const canvas = document.getElementById('glslCanvas');
        const gl = canvas.getContext('webgl');
        if (!gl) {
            alert("抱歉,您的浏览器不支持 WebGL。");
        }

        // 获取 DOM 元素
        const backgroundImage = document.getElementById('backgroundImage');
        const useCircleCheckbox = document.getElementById('useCircleCheckbox');
        
        // --- 1. 创建着色器程序 ---
        function createShader(gl, type, source) {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);
            const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
            if (success) {
                return shader;
            }
            console.error(gl.getShaderInfoLog(shader));
            gl.deleteShader(shader);
        }

        const vertexShaderSource = document.getElementById('vertex-shader').text;
        const fragmentShaderSource = document.getElementById('fragment-shader').text;

        const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
        const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

        function createProgram(gl, vertexShader, fragmentShader) {
            const program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
            const success = gl.getProgramParameter(program, gl.LINK_STATUS);
            if (success) {
                return program;
            }
            console.error(gl.getProgramInfoLog(program));
            gl.deleteProgram(program);
        }

        const program = createProgram(gl, vertexShader, fragmentShader);
        gl.useProgram(program);

        // --- 2. 准备数据 ---
        // 获取 uniform 和 attribute 的位置
        const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
        const resolutionUniformLocation = gl.getUniformLocation(program, "iResolution");
        const timeUniformLocation = gl.getUniformLocation(program, "iTime");
        const mouseUniformLocation = gl.getUniformLocation(program, "iMouse");
        const imageResolutionUniformLocation = gl.getUniformLocation(program, "iImageResolution");
        const imageSamplerUniformLocation = gl.getUniformLocation(program, "iImage1");
        const useCircleUniformLocation = gl.getUniformLocation(program, "useCircle");
        
        // 创建一个缓冲区来放置一个覆盖整个画布的矩形
        const positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

        gl.enableVertexAttribArray(positionAttributeLocation);
        gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

        // --- 3. 设置纹理 ---
        let imageTexture;
        backgroundImage.onload = function() {
            imageTexture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, imageTexture);
            // 设置纹理参数
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            // 将图片数据上传到纹理
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, backgroundImage);

            // 设置图像分辨率 uniform
            gl.uniform3f(imageResolutionUniformLocation, backgroundImage.width, backgroundImage.height, 0);
            
            // 启动渲染
            requestAnimationFrame(render);
        };
        // 如果图片已经加载完成 (例如从缓存加载)
        if (backgroundImage.complete) {
            backgroundImage.onload();
        }

        // --- 4. 渲染循环 ---
        let startTime = Date.now();
        let mouseX = 0;
        let mouseY = 0;

        function render(time) {
            // 调整画布大小以匹配显示大小
            const displayWidth = gl.canvas.clientWidth;
            const displayHeight = gl.canvas.clientHeight;
            if (gl.canvas.width !== displayWidth || gl.canvas.height !== displayHeight) {
                gl.canvas.width = displayWidth;
                gl.canvas.height = displayHeight;
                gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
            }
            
            gl.clearColor(0, 0, 0, 0);
            gl.clear(gl.COLOR_BUFFER_BIT);

            // 更新 uniforms
            gl.uniform3f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height, 1.0);
            gl.uniform1f(timeUniformLocation, (Date.now() - startTime) * 0.001);
            gl.uniform4f(mouseUniformLocation, mouseX, gl.canvas.height - mouseY, 0, 0); // y坐标需要翻转
            gl.uniform1f(useCircleUniformLocation, useCircleCheckbox.checked ? 1.0 : 0.0);

            // 绑定纹理
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, imageTexture);
            gl.uniform1i(imageSamplerUniformLocation, 0);

            // 绘制
            gl.drawArrays(gl.TRIANGLES, 0, 6);

            // 请求下一帧
            requestAnimationFrame(render);
        }

        // --- 5. 事件监听 ---
        window.addEventListener('mousemove', (e) => {
            mouseX = e.clientX;
            mouseY = e.clientY;
        });
    </script>
</body>
</html>

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
null3213 + 1 + 1 热心回复!

查看全部评分

w027000 发表于 2025-6-18 21:31
zhangcool2008 发表于 2025-6-18 21:39
1096315079a 发表于 2025-6-18 21:49

看不到效果图。+1
dysunb 发表于 2025-6-18 21:51
看不到效果图
xiaokui5 发表于 2025-6-18 22:13
看不到效果图。+2
小智xyz 发表于 2025-6-18 22:21
没有效果图
cn2jp 发表于 2025-6-18 22:28
正在转圈圈的不会就是效果图吧
 楼主| 当钟声敲响回忆 发表于 2025-6-18 22:30
cn2jp 发表于 2025-6-18 22:28
正在转圈圈的不会就是效果图吧

抱歉我在国外,用的图床不知道境内访问会很慢...已经在换图了,请您过一会再看看这个帖子应该就有了
 楼主| 当钟声敲响回忆 发表于 2025-6-18 22:31

抱歉我在国外,用的图床不知道境内访问会很慢...已经在换图了,请您过一会再看看这个帖子应该就有了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-29 12:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表