【Android】RuntimeShader 应用

news2025/3/19 23:49:29

1 简介

        RuntimeShader 是 Android 13(T)中新增的特性,用于逐像素渲染界面,它使用 AGSL(Android Graphics Shading Language)编写着色器代码,底层基于 Skia 图形渲染引擎。官方介绍详见 → RuntimeShader。

        相较于 OpenGL ES,RuntimeShader 具有以下特点。

  • RuntimeShader 中只有片元着色器,没有顶点着色器。
  • RuntimeShader 中用户不用输入顶点数据,简化了输入操作。
  • RuntimeShader 基于 AGSL 语言,OpenGL ES 基于 GLSL 语言。
  • AGSL 中纹理坐标值域与 View 的宽高对应,GLSL 中纹理坐标一般归一化了。

        本文完整资源见 → RuntimeShader应用。

2 对 View 进行二次渲染

        MainActivity.java

package com.zhyan8.shaderdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.RenderEffect;
import android.graphics.RuntimeShader;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

import com.zhyan8.shaderdemo.utils.ScreenUtils;
import com.zhyan8.shaderdemo.utils.StringUtils;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout parentView = findViewById(R.id.parent);
        float[] resolution = ScreenUtils.getScreenSizeF(this);
        applyRuntimeShader(parentView, resolution);
    }

    private void applyRuntimeShader(View view, float[] resolution) {
        String shaderCode = StringUtils.loadString(this, "shaders/dazzling.agsl");
        RuntimeShader shader = new RuntimeShader(shaderCode);
        shader.setFloatUniform("u_resolution", resolution);
        RenderEffect effect = RenderEffect.createRuntimeShaderEffect(shader, "u_texture");
        view.setRenderEffect(effect);
    }
}

        activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:gravity="center"
    android:background="#FFFFFF"
    android:id="@+id/parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"
        android:textSize="60sp"
        android:textColor="#669933"/>

</LinearLayout>

        dazzling.agsl

uniform shader u_texture;
uniform vec2 u_resolution;

vec4 main(vec2 coords) {
    vec4 tex = u_texture.eval(coords);
    vec2 normUV = coords / u_resolution;
    vec3 color = tex.rgb * vec3(normUV.x, normUV.y, 0.5);
    return vec4(color, 1.0);
}

        说明:coords 的值域与 View 的宽高对应,并不是归一化的坐标。 

        运行效果如下。

3 通过 Canvas 进行渲染

3.1 简单应用

        MainActivity.java

package com.zhyan8.shaderdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.WindowManager;
import android.widget.LinearLayout;

import com.zhyan8.shaderdemo.graphics.ShaderView;

public class MainActivity extends AppCompatActivity {
    private ShaderView mShaderView;
    private LinearLayout mParentView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mParentView = findViewById(R.id.parent);
        addView();
        MyRenderer renderer = new MyRenderer(this);
        mShaderView.setRenderer(renderer);
        mShaderView.requestRender(true);
    }

    private void addView() {
        mShaderView = new ShaderView(this);
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
        lp.height = WindowManager.LayoutParams.MATCH_PARENT;
        mParentView.addView(mShaderView, lp);
    }
}

        ShaderView.java

package com.zhyan8.shaderdemo.graphics;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RuntimeShader;
import android.os.Handler;
import android.os.Looper;
import android.view.Choreographer;
import android.view.View;

/**
 * 自定义view, 承载渲染环境作用, 类比GLSurfaceView
 * @author little fat sheep
 */
public class ShaderView extends View {
    private Paint mPaint = new Paint();
    private Renderer mRenderer;
    private float[] mResolution;
    private long mStartTime = 0L;
    private long mRunTime = 0L;
    private Choreographer mChoreographer;
    private Handler mHandler;

    public ShaderView(Context context) {
        super(context);
        mChoreographer = Choreographer.getInstance();
        mHandler = new Handler(Looper.getMainLooper());
    }

    public void setRenderer(Renderer renderer) {
        this.mRenderer = renderer;
        RuntimeShader shader = renderer.onSurfaceCreated();
        mPaint.setShader(shader);
        mStartTime = System.currentTimeMillis();
    }

    public void requestRender() {
        invalidate();
    }

    public void requestRender(long duration) {
        mHandler.removeCallbacksAndMessages(null);
        mHandler.post(() -> {
            mChoreographer.postFrameCallback(mFrameCallback);
        });
        mHandler.postDelayed(() -> {
            mChoreographer.removeFrameCallback(mFrameCallback);
        }, duration);
    }

    public void requestRender(boolean continuous) {
        if (continuous) {
            mChoreographer.postFrameCallback(mFrameCallback);
        } else {
            invalidate();
        }
    }

    public void stopRenderer() {
        mChoreographer.removeFrameCallback(mFrameCallback);
    }

    public void stopRenderer(long delay) {
        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(() -> {
            mChoreographer.removeFrameCallback(mFrameCallback);
        }, delay);
    }

    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mResolution = new float[] { w, h };
        mRenderer.onSurfaceChanged(w, h);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRunTime = System.currentTimeMillis() - mStartTime;
        mRenderer.onDrawFrame(mRunTime);
        canvas.drawRect(0f, 0f, mResolution[0], mResolution[1], mPaint);
    }

    private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            mChoreographer.postFrameCallback(this);
            ShaderView.this.invalidate();
        }
    };

    /**
     * 渲染器接口, 类比GLSurfaceView.Renderer
     * @author little fat sheep
     */
    public interface Renderer {
        RuntimeShader onSurfaceCreated();
        void onSurfaceChanged(int width, int height);
        void onDrawFrame(long runTime);
    }
}

        BitmapTexture.java

package com.zhyan8.shaderdemo.graphics;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.RuntimeShader;
import android.graphics.Shader;

import com.zhyan8.shaderdemo.utils.BitmapUtils;

/**
 * Bitmap纹理
 * @author little fat sheep
 */
public class BitmapTexture {
    private BitmapShader mBitmapShader;
    private float[] mSize;

    private BitmapTexture(BitmapShader bitmapShader, float[] size) {
        this.mBitmapShader = bitmapShader;
        this.mSize = size;
    }

    public static BitmapTexture create(Context context, String assetPath) {
        Bitmap bitmap = BitmapUtils.loadBitmapFromAsset(context, assetPath);
        return create(bitmap);
    }

    public static BitmapTexture create(Context context, int rawId) {
        Bitmap bitmap = BitmapUtils.loadBitmapFromRaw(context, rawId);
        return create(bitmap);
    }

    public static BitmapTexture create(Bitmap bitmap) {
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        float[] size = new float[] { bitmap.getWidth(), bitmap.getHeight() };
        return new BitmapTexture(bitmapShader, size);
    }

    public void bind(RuntimeShader shader, String textureName, String sizeName) {
        shader.setInputShader(textureName, mBitmapShader);
        shader.setFloatUniform(sizeName, mSize);
    }
}

        MyRenderer.java

package com.zhyan8.shaderdemo;

import android.content.Context;
import android.graphics.RuntimeShader;

import com.zhyan8.shaderdemo.graphics.BitmapTexture;
import com.zhyan8.shaderdemo.graphics.ShaderView;
import com.zhyan8.shaderdemo.utils.StringUtils;

public class MyRenderer implements ShaderView.Renderer {
    private Context mContext;
    private RuntimeShader mShader;
    private float[] mResolution;
    private BitmapTexture mBitmapTexture;

    public MyRenderer(Context context) {
        this.mContext = context;
    }

    @Override
    public RuntimeShader onSurfaceCreated() {
        String shaderCode = StringUtils.loadString(mContext, "shaders/jelly.agsl");
        mShader = new RuntimeShader(shaderCode);
        mBitmapTexture = BitmapTexture.create(mContext, "textures/photo.png");
        return mShader;
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        mResolution = new float[] { width, height };
    }

    @Override
    public void onDrawFrame(long runTime) {
        mBitmapTexture.bind(mShader, "u_texture", "u_textureSize");
        mShader.setFloatUniform("u_resolution", mResolution);
        mShader.setFloatUniform("u_time", runTime / 1000f);
    }
}

        jelly.agsl

uniform shader u_texture;
uniform vec2 u_textureSize;
uniform vec2 u_resolution;
uniform float u_time;

vec4 texture(vec2 normUV) { // 纹理采样
    vec2 uv = normUV * u_textureSize;
    return u_texture.eval(uv);
}

vec2 fun(vec2 uv, float aspect) { // 畸变函数
    vec2 center = vec2(0.5, 0.5 / aspect);
    vec2 dire = normalize(uv - center);
    float dist = distance(uv, center);
    vec2 uv1 = uv + sin(dist * 2.2 + u_time * 3.5) * 0.025;
    return uv1;
}

vec4 main(vec2 coords) {
    vec2 normUV = coords / u_resolution;
    float aspect = u_resolution.x / u_resolution.y;
    normUV.y /= aspect;
    vec2 uv = fun(normUV, aspect);
    uv.y *= aspect;
    return texture(uv);
}

        运行效果如下。

3.2 二次渲染

        本节将对 3.1 节中 MyRenderer 进行修改,使用两个 RuntimeShader 实现二次渲染。

        MyRenderer.java

package com.zhyan8.shaderdemo;

import android.content.Context;
import android.graphics.RuntimeShader;

import com.zhyan8.shaderdemo.graphics.BitmapTexture;
import com.zhyan8.shaderdemo.graphics.ShaderView;
import com.zhyan8.shaderdemo.utils.StringUtils;

public class MyRenderer implements ShaderView.Renderer {
    private Context mContext;
    private RuntimeShader mShader1;
    private RuntimeShader mShader2;
    private float[] mResolution;
    private BitmapTexture mBitmapTexture;

    public MyRenderer(Context context) {
        this.mContext = context;
    }

    @Override
    public RuntimeShader onSurfaceCreated() {
        String shaderCode1 = StringUtils.loadString(mContext, "shaders/dispersion.agsl");
        mShader1 = new RuntimeShader(shaderCode1);
        String shaderCode2 = StringUtils.loadString(mContext, "shaders/jelly.agsl");
        mShader2 = new RuntimeShader(shaderCode2);
        mBitmapTexture = BitmapTexture.create(mContext, "textures/photo.jpg");
        return mShader2;
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        mResolution = new float[] { width, height };
    }

    @Override
    public void onDrawFrame(long runTime) {
        mBitmapTexture.bind(mShader1, "u_texture", "u_textureSize");
        mShader1.setFloatUniform("u_resolution", mResolution);
        mShader1.setFloatUniform("u_time", runTime / 1000f);

        mShader2.setInputShader("u_texture", mShader1);
        mShader2.setFloatUniform("u_textureSize", mResolution);
        mShader2.setFloatUniform("u_resolution", mResolution);
        mShader2.setFloatUniform("u_time", runTime / 1000f);
    }
}

        dispersion.agsl

uniform shader u_texture;
uniform vec2 u_textureSize;
uniform vec2 u_resolution;
uniform float u_time;

vec4 texture(vec2 normUV) { // 纹理采样
    vec2 uv = normUV * u_textureSize;
    return u_texture.eval(uv);
}

vec2 getOffset() { // 偏移函数
    float time = u_time * 1.5;
    vec2 dire = vec2(sin(time), cos(time));
    float strength = sin(u_time * 2.0) * 0.01;
    return dire * strength;
}

vec4 main(vec2 coords) {
    vec2 normUV = coords / u_resolution;
    vec4 color = texture(normUV);
    vec2 offset = getOffset();
    color.r = texture(normUV + offset).r;
    color.b = texture(normUV - offset).b;
    return color;
}

        运行效果如下,可以看到叠加了果冻畸变和 RGB 色散效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2318037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Skia 图形引擎介绍

文章目录 一、Skia 的基本概念1. 定位与作用2. 历史背景 二、Skia 的核心架构1. 模块化设计2. 渲染流程3. 跨平台适配 三、Skia 在 Flutter 中的角色1. 自绘 UI 的核心依赖2. 跨平台一致性3. 性能优化 四、Skia 的性能优势1. 高效的图形处理2. 与原生渲染的对比3. 性能瓶颈 五、…

10、基于osg引擎生成热力图高度图实现3D热力图可视化、3D热力图实时更新(带过渡效果)

1、结果 2、完整C代码 #include <sstream> #include <iomanip> #include <iostream> #include <vector> #include <random> #include <cmath> #include <functional> #include <osgViewer/viewer> #include <osgDB/Read…

Modbus通信协议基础知识总结

1. 数据类型与存储区分类 Modbus协议将数据分为四类存储区&#xff0c;通过存储区代号区分&#xff1a; 输出线圈&#xff08;0x&#xff09;&#xff1a;可读写&#xff0c;对应二进制开关量&#xff08;如继电器状态&#xff09;&#xff0c;地址范围000001-065536&#xff…

AndroidStudio下载安装,环境部署以及常见问题解决教程(亲测)

AndroidStudio下载安装&#xff0c;环境部署以及常见问题解决&#xff01;&#xff01;&#xff01; 文章目录 前言 一、Android Studio 下载与安装 1.1 系统要求 1.2 下载 Android Studio 1.3 安装 Android Studio Windows 系统 1.4 初始配置 二、环境部署 2.1 安装 …

开源免费一句话生成儿童故事视频核心思想解析

再看一个演示视频&#xff0c;学会核心思想后&#xff0c;可以打造自己的内容生成工具&#xff0c;后文有基于飞书多维表格的实现效果&#xff1a; 一句话灵感生成儿童故事视频演示 这是一款专门为内容素材创作打造的创新工具&#xff0c;可根据用户输入的主题&#xff0c;快速…

数据结构——最短路(BFS,Dijkstra,Floyd)

完整版可以看我的最短路问题模版总结_稠密图最短路-CSDN博客 考研数据结构只考BFS,Dijkstra和Floyd 下面代码以Acwing模板题为例 BFS代码 适用类型&#xff1a; 1.单源最短路径 2.无权图 3.不适用于带权图和负权回路图 //Acwing走迷宫bfs #include<bits/stdc.h>usi…

Kali Linux汉化教程:轻松设置中文界面

1.打开终端 2.输入sudo dpkg-reconfigure locales&#xff0c;回车&#xff0c;输入密码&#xff0c;回车 sudo dpkg-reconfigure locales 3.往下滑&#xff0c;滑到底&#xff0c;找到‘zh_CN.UTF-8 UTF-8’,回车 4.选择‘zh_CN.UTF-8’,回车 5.没有 ‘zh_CN.UTF-8’选项的特…

C++和标准库速成(八)——指针、动态数组、const、constexpr和consteval

目录 1. 指针和动态数组1.1 栈和自由存储区1.2 使用指针1.3 动态分配的数组1.4 空指针常量 2. const2.1 const修饰类型2.2 const与指针2.3 使用const保护参数2.4 const方法(建议&#xff09; 3. constexpr4. consteval参考 1. 指针和动态数组 动态内存允许所创建的程序具有在编…

超声重建,3D重建 超声三维重建,三维可视化平台 UR 3D Reconstruction

1. 超声波3D重建技术的实现方法与算法 技术概述 3D超声重建是一种基于2D超声图像生成3D体积数据的技术&#xff0c;广泛应用于医学影像领域。通过重建和可视化三维结构&#xff0c;3D超声能够显著提高诊断精度和效率&#xff0c;同时减少医生的脑力负担。本技术文档将详细阐述…

[HelloCTF]PHPinclude-labs超详细WP-Level 6Level 7Level 8Level 9-php://协议

由于Level 6-9 关的原理都是通用的, 这里就拿第6关举例, 其他的关卡同理 源码分析 定位到代码 isset($_GET[wrappers]) ? include("php://".$_GET[wrappers]) : ; 与前几关发生变化的就是 php:// 解题分析 这一关要求我们使用 php协议 php:// 协议 php://filte…

【Linux】Bash是什么?怎么使用?

李升伟 整理 什么是 Bash&#xff1f; Bash&#xff08;Bourne Again Shell&#xff09;是一种 命令行解释器&#xff08;Shell&#xff09;&#xff0c;广泛用于 Unix 和 Linux 操作系统。它是 Bourne Shell&#xff08;sh&#xff09; 的增强版&#xff0c;提供了更多的功能…

如何创建并保存HTML文件?零基础入门教程

原文&#xff1a;如何创建并保存HTML文件&#xff1f;零基础入门教程 | w3cschool笔记 本文将以Windows系统为例&#xff0c;教你用最简单的记事本创建并保存第一个HTML网页。 &#x1f4dd; 第一步&#xff1a;准备工具 文本编辑器&#xff1a;使用系统自带的记事本&#xff…

React19源码系列之FiberRoot节点和Fiber节点

在上一篇文章&#xff0c;看了createRoot函数的大致流程。 createContainer函数创建并返回了FiberRoot 。FiberRoot是由createFiberRoot函数创建&#xff0c; createFiberRoot函数还将 FiberRoot和 根Fiber 通过current属性建立起了联系。将FiberRoot作为参数传给 ReactDOMRoo…

TCP协议的多线程应用、多线程下的网络编程

DAY13.2 Java核心基础 多线程下的网络编程 基于单点连接的方式&#xff0c;一个服务端对应一个客户端&#xff0c;实际运行环境中是一个服务端需要对应多个客户端 创建ServerSocketNable类&#xff0c;多线程接收socket对象 public class ServerSocketNable implements Run…

华为中小型企业项目案例

实验目的(1) 熟悉华为交换机和路由器的应用场景 (2) 掌握华为交换机和路由器的配置方法 实验拓扑实验拓扑如图所示。 华为中小型企业项目案例拓扑图 实验配置市场部和技术部的配置创建VLANLSW1的配置 [LSW1]vlan batch 10 20 [LSW1]q…

LabVIEW VI Scripting随机数波形图自动生成

通过LabVIEW VI Scripting 技术&#xff0c;实现从零开始编程化创建并运行一个随机数波形监测VI。核心功能包括自动化生成VI框架、添加控件与函数、配置数据流逻辑及界面布局优化&#xff0c;适用于批量生成测试工具、教学模板开发或复杂系统的模块化构建。通过脚本化操作&…

MATLAB 控制系统设计与仿真 - 26

状态空间控制系统概述 状态空间描述 现代控制理论是建立在状态空间基础上的控制系统分析和设计理论&#xff0c;它用状态变量来刻画系统的内部特征&#xff0c;用‘一节微分方程组’来描述系统的动态特性。系统的状态空间模型描述了系统输入/输出与内部状态之间的关系&#x…

Python----计算机视觉处理(Opencv:图像镜像旋转)

一、图像镜像旋转 图像的旋转是围绕一个特定点进行的&#xff0c;而图像的镜像旋转则是围绕坐标轴进行的。图像镜像旋转&#xff0c;也可 以叫做图像翻转&#xff0c;分为水平翻转、垂直翻转、水平垂直翻转三种。 通俗的理解为&#xff0c;当以图片的中垂线为x轴和y轴时&#x…

C++从入门到入土(八)——多态的原理

目录 前言 多态的原理 动态绑定与静态绑定 虚函数表 小结 前言 在前面的文章中&#xff0c;我们介绍了C三大特性之一的多态&#xff0c;我们主要介绍了多态的构成条件&#xff0c;但是对于多态的原理我们探讨的是不够深入的&#xff0c;下面这这一篇文章&#xff0c;我们将…

PyCharm安装redis,python安装redis,PyCharm使用失败问题

报错信息 Usage: D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] -r [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip instal…