You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android的OpenGL ES中向着色器传递时间变量实现雨滴动画

在Android OpenGL ES中实现动态雨滴效果(含时间变量传递方案)

核心问题解决:传递时间变量到着色器

Three.js会自动管理时间变量,但OpenGL ES需要你在Android代码中手动计算并传递u_time uniform变量,具体步骤如下:

  • 计算累计时间:初始化时记录起始时间,在渲染循环中用当前时间减去起始时间,转换成秒数:

    // 初始化阶段记录起始时间
    private long startTime = System.nanoTime();
    
    // 渲染循环内计算已流逝时间(单位:秒)
    long currentTime = System.nanoTime();
    float elapsedTime = (currentTime - startTime) / 1_000_000_000f;
    
  • 更新uniform变量:在着色器程序链接完成后,获取u_time的位置,每次渲染时传入最新时间值:

    // 初始化时获取u_time的uniform位置
    int uTimeLocation = GLES20.glGetUniformLocation(program, "u_time");
    
    // 渲染循环内更新时间变量
    GLES20.glUniform1f(uTimeLocation, elapsedTime);
    

现有着色器代码优化建议

你的着色器已经实现了雨滴的随机生成和动态强度逻辑,但可以调整以下细节让效果更自然:

  1. 添加雨滴下落动画
    当前代码的雨滴位置固定,把时间变量融入Y轴偏移,实现下落效果:

    // 修改Drops函数中的shiftY计算
    float shiftY = fract(Random11(0.5, seed) + u_time * 0.2); // 0.2控制下落速度
    uv.y += shiftY;
    
  2. 混合雨滴与原纹理
    当前直接输出雨滴颜色,改为和传入的纹理混合,让雨滴叠加在原图上:

    void main(void){
        vec2 uv = v_TexCoord;
        vec4 textureColor = texture2D(u_Texture, uv);
        vec2 drop = Drops(uv, 123.3);
        // 用半透明蓝色模拟雨滴,混合到原图
        vec4 dropColor = vec4(0.2, 0.4, 0.8, drop.x * 0.6);
        gl_FragColor = mix(textureColor, dropColor, drop.x);
    }
    
  3. 优化随机函数稳定性
    调整随机函数的参数,避免sin函数的周期性问题:

    float Random11(float inputValue, float seed){
        return fract(sin(inputValue * 12.9898 + seed) * 43758.5453);
    }
    

完整优化后的片段着色器代码

precision highp float;
#define HIGHFLOAT highp
uniform sampler2D u_Texture;
varying vec2 v_TexCoord;
uniform HIGHFLOAT float u_time;

float Random11(float inputValue, float seed){
    return fract(sin(inputValue * 12.9898 + seed) * 43758.5453);
}

float Random21(vec2 inputValue, float seed){
    return fract(sin(dot(inputValue, vec2(12.9898, 78.233)) + seed) * 43758.5453);
}

vec2 Drops(vec2 uv, float seed){
    // 加入时间变量实现雨滴下落
    float shiftY = fract(Random11(0.5, seed) + u_time * 0.2);
    uv.y += shiftY;
    float cellRes = 10.0;
    uv *= cellRes;

    float rowInd = floor(uv.y);
    float shiftX = Random11(rowInd, seed);
    uv.x += shiftX;
    vec2 cellIndex = floor(uv);
    vec2 cellUv = fract(uv);

    // 定义雨滴圆形区域
    vec2 cellCenter = vec2(0.5);
    float distFromCenter = distance(cellUv, cellCenter);
    float isInside = 1.0 - step(0.1, distFromCenter);

    // 随机控制雨滴显示
    float isDropShown = step(0.8, Random21(cellIndex, seed + 1422.14));

    // 雨滴淡入淡出动画
    float dropIntensity = 1.0 - fract(u_time * 0.1 + Random21(cellIndex, seed + 3221.12) * 2.0) * 2.0;
    dropIntensity = sin(dropIntensity * 3.14159) * pow(abs(dropIntensity), 4.0);
    dropIntensity = clamp(dropIntensity, 0.0, 1.0);

    return vec2(isInside) * isDropShown * dropIntensity;
}

void main(void){
    vec2 uv = v_TexCoord;
    vec4 baseColor = texture2D(u_Texture, uv);
    vec2 dropValue = Drops(uv, 123.3);
    // 叠加半透明雨滴效果
    vec4 dropColor = vec4(0.1, 0.3, 0.6, dropValue.x * 0.7);
    gl_FragColor = baseColor + dropColor;
}

Android端渲染循环关键代码示例

确保在每次渲染时更新时间变量:

@Override
public void onDrawFrame(GL10 gl) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    // 计算已流逝的时间(秒)
    long currentTime = System.nanoTime();
    float elapsedTime = (currentTime - startTime) / 1_000_000_000f;

    // 更新着色器中的时间变量
    GLES20.glUniform1f(uTimeLocation, elapsedTime);

    // 绘制承载纹理的平面
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}

内容的提问来源于stack exchange,提问作者Areeb Lone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:20:41