【Sceneform-EQR】scenefrom-eqr中的几种背景实现(不仅用于AR、三维场景,在图片、视频播放器中也适用)

Sceneform-EQR

简介

Sceneform-EQR是EQ基于sceneform(filament)扩展的一个用于安卓端的三维渲染器。

相关链接

Git仓库
码云
EQ-R相关文档

几种背景实现方式

示例工程

Sceneform-EQR

实现透明背景

代码样例

示例地址:Sceneform-EQR中BaseSceneActivity.java

安卓布局文件:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#1E4670"
    tools:context=".BaseSceneActivity">
    <TextView
        android:textColor="#fff"
        android:textSize="24dp"
        android:layout_marginTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="EQ-Renderer"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        
    <com.eqgis.eqr.layout.SceneLayout
        android:id="@+id/base_scene_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        
</androidx.constraintlayout.widget.ConstraintLayout>

Java接口调用:

外部直接调用setTransparent(true);即可实现背景透明

java 复制代码
        sceneLayout = findViewById(R.id.base_scene_layout);
        sceneLayout.setTransparent(true);
注意事项

SurfaceView的问题

由于当前SceneView继承SurfaceView,而在SurfaceView中常见的设置透明背景会有的层级问题。这里也会存在。

使用setTransparent(true)后,SceneView被置顶

解决思路

SceneView修改为继承TextureView,可以规避SurfaceView的问题(PS:这里考虑性能,主版本代码已修改回SurfaceView)。

参考代码提交日志:

由于性能,改回使用surfaceView

使用扩展背景

这里需要时用ExSceneView类,参考如下即可实现,暂不放效果截图了。

最初的需求

之前有实现视频背景、图片背景的需求,曾在sceneview的discussions中提过我的实现思路。(链接

当时,我创建了一个ExSceneView的类,实现扩展背景的功能。(现已集成至Sceneform-EQR)

图片背景样例
  1. 获取Externaltexture

    ExSceneView sceneView = (ExSceneView) scene.getView();
    externalTexture = sceneView.getExternalTexture();

  2. 在canvas中绘制Bitmap

如下,那么同理可以在cavans绘制其他内容,而不仅仅是绘制一张图片。

    private void loadStaticBackground(Bitmap bitmap) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        externalTexture.getSurfaceTexture().setDefaultBufferSize(width,height);

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            Canvas canvas = externalTexture.getSurface().lockHardwareCanvas();
            canvas.drawBitmap(bitmap,new Matrix(),new Paint());
            externalTexture.getSurface().unlockCanvasAndPost(canvas);
        }

        bitmap.recycle();
    }
视频背景样例

通过上一个样例可知。我们还可以绘制其他内容。那么同理我们也可以获取externalTexture的surface,配合MediaPlayer,那么我们就可以实现播放视频背景。

  1. 获取Externaltexture

    ExSceneView sceneView = (ExSceneView) scene.getView();
    backgroundTexture = sceneView.getExternalTexture();

  2. 配合MediaPlayer使用

关键:mediaPlayer.setSurface(backgroundTexture.getSurface());

    private void loadDynamicBackground() {
        ExSceneView sceneView = (ExSceneView) scene.getView();
        ExternalTexture backgroundTexture = sceneView.getExternalTexture();

        mediaPlayer = MediaPlayer.create(sceneView.getContext(),R.raw.bg_video);
        mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(MediaPlayer mediaPlayer, int w, int h) {
                if (backgroundTexture != null) {
                    backgroundTexture.getSurfaceTexture().setDefaultBufferSize(w,h);
                }
            }
        });
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                mediaPlayer.setSurface(backgroundTexture.getSurface());
                mediaPlayer.setLooping(true);
                mediaPlayer.start();
            }
        });
    }
颜色剔除

通过颜色剔除,我们可以在显示时过滤指定颜色值,从而达到类似抠图的效果。

  • 实现这个需要重写材质文件并编译。(之前写过材质使用的文档,这里不赘述。)

下面材质中samplerExternal类型,对应sceneform中的ExternalTexture类。keyColor属性名称对应的颜色即为要剔除的颜色值。

material {
    name : "Material",
    parameters : [
        {
            // 扩展纹理
            type : samplerExternal,
            name : texture
        },
        {
            // 要剔除的颜色值
            type : float4,
            name : keyColor
        }
    ],
    requires : [ uv0 ],
    shadingModel : unlit,
    doubleSided : true,
    blending : masked
}

fragment {
    vec3 desaturate(vec3 color, float amount) {
        vec3 gray = vec3(dot(vec3(0.2126, 0.7152, 0.0722), color));
        return vec3(mix(color, gray, amount));
    }
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        vec2 uv = getUV0();
        if (!gl_FrontFacing) {
          uv.x = 1.0 - uv.x;
        }
        vec4 color = texture(materialParams_texture, uv).rgba;
        vec3 keyColor = materialParams.keyColor.rgb;
        float threshold = 0.675;
        float slope = 0.2;
        float distance = abs(length(abs(keyColor - color.rgb)));
        float edge0 = threshold * (1.0 - slope);
        float alpha = smoothstep(edge0, threshold, distance);
        color.rgb = desaturate(color.rgb, 1.0 - (alpha * alpha * alpha));
        material.baseColor.a = alpha;
        material.baseColor.rgb = inverseTonemapSRGB(color.rgb);
        material.baseColor.rgb *= material.baseColor.a;
    }
}
颜色混合

通过颜色剔除,我们可以在显示时叠加某个颜色值(或者纹理),从而达到类似滤镜的效果。

material {
    name : "EQ Blend Mat for Camera",
    shadingModel : unlit,
    blending : opaque,
    parameters : [
        {
            type : samplerExternal,
            name : cameraTexture
        },
        {
            type : float4x4,
            name : uvTransform
        },
        {
            // 用于混合背景的颜色
            type : float4,
            name : blendColor
        }
    ],
    requires : [
        uv0
    ]
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        vec4 color = texture(materialParams_cameraTexture, getUV0()).rgba;
        vec4 bColor = materialParams.blendColor.rgba;
        color = mix(color, bColor, 0.2);
        material.baseColor.rgb = inverseTonemapSRGB(color.rgb);
        material.baseColor.a = 1.f;
    }
}

vertex {
    void materialVertex(inout MaterialVertexInputs material) {
        material.uv0 = mulMat4x4Float3(materialParams.uvTransform, vec3(material.uv0.x, material.uv0.y, 0.f)).xy;
        vec4 clip = getPosition();
        clip.z = 0.99999f;
        material.worldPosition = mulMat4x4Float3(getWorldFromClipMatrix(), clip.xyz);
    }
} 
相关推荐
拭心1 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
darkdragonking3 小时前
FLV视频封装格式详解
音视频
带电的小王3 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡4 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道4 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库5 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道5 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe6 小时前
Android Hook - 动态加载so库
android
居居飒6 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He9 小时前
桌面列表小部件不能点击的问题分析
android