【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);
    }
} 
相关推荐
阿巴斯甜13 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker14 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952715 小时前
Andorid Google 登录接入文档
android
黄林晴16 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android