当前位置: 首页 > news >正文

【Android】RuntimeShader 应用

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 {@Overrideprotected 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"?>
<LinearLayoutxmlns: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"><TextViewandroid: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 的宽高对应,并不是归一化的坐标。

​ 运行效果如下。

img

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;@Overrideprotected 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);}@Overridepublic 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);}@Overridepublic 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() {@Overridepublic 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;}@Overridepublic RuntimeShader onSurfaceCreated() {String shaderCode = StringUtils.loadString(mContext, "shaders/jelly.agsl");mShader = new RuntimeShader(shaderCode);mBitmapTexture = BitmapTexture.create(mContext, "textures/photo.png");return mShader;}@Overridepublic void onSurfaceChanged(int width, int height) {mResolution = new float[] { width, height };}@Overridepublic 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);
}

​ 运行效果如下。

img

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;}@Overridepublic 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;}@Overridepublic void onSurfaceChanged(int width, int height) {mResolution = new float[] { width, height };}@Overridepublic 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 色散效果。

img

​ 声明:本文转自【Android】RuntimeShader 应用。

http://www.hskmm.com/?act=detail&tid=24158

相关文章:

  • 【Rive】rive-android源码分析
  • zkSync Era主网上线:首个zkEVM全面开放的技术突破
  • Microsoft Access SQL 查询中的通配符 - 详解
  • 洛谷P11738 [集训队互测 2015] 未来程序改
  • mcp 面试题
  • 【开题答辩过程】以《基于SpringBoot+Vue+uni-app的智慧校园服务系统的设计与搭建》为例,不会开题答辩的可能进来看看
  • 6_什么是知识图谱
  • 微信ipad协议个微机器人开发API
  • 学习方法
  • ai提交消息常用的 chore,原来是个单词(琐事/零散任务)+约定,用于非功能性提交
  • 微信开发之朋友圈自动评论的技术实现
  • 多项式定理
  • The Brain in Your Toes: Can Tiny Foot Movements Boost BDNF and Sharpen the Mind? - 教程
  • 详细介绍:Kafka09-速答-尚硅谷
  • day15 课程(继承 )
  • node菜单服务引起的后台异常表象到运维释放从库的数据库连接及驱动修改配置,重新部署生效
  • 微商本地化发展模式的借鉴与探讨——以开源AI智能名片链动2+1模式S2B2C商城小工具为例
  • Docker 部署 RAGFlow 全流程教程
  • 树的直径
  • 深入解析:从零起步学习Redis || 第四章:Cache Aside Pattern(旁路缓存模式)以及优化策略
  • 深度解码电子设计可靠性:形式验证(Formal Verification)如何护航 IC 高质量之路
  • 251004
  • gradle Cause: zip END header not found
  • 10 4
  • 叠爱心(love.*)
  • 从单层感知机到多层感知机(MLP)
  • 机电公司管理小工具|基于微信小应用的机电公司管理小程序设计与实现(源码+数据库+文档)
  • 【性质】CF689D Friends and Subsequences
  • Arduino+数码管 = 量电压 | A+B problem | alphabet
  • 详细介绍:【数据库知识】TxSQL 主从数据库同步底层原理深度解析