Sceneform-EQR
简介
Sceneform-EQR是EQ基于sceneform(filament)扩展的一个用于安卓端的三维渲染器。
相关链接
Git仓库
码云
EQ-R相关文档
几种背景实现方式
示例工程
实现透明背景
代码样例
示例地址: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);即可实现背景透明
sceneLayout = findViewById(R.id.base_scene_layout);
sceneLayout.setTransparent(true);
注意事项
SurfaceView的问题
由于当前SceneView继承SurfaceView,而在SurfaceView中常见的设置透明背景会有的层级问题。这里也会存在。
使用setTransparent(true)后,SceneView被置顶
解决思路
SceneView修改为继承TextureView,可以规避SurfaceView的问题(PS:这里考虑性能,主版本代码已修改回SurfaceView)。
参考代码提交日志:
使用扩展背景
这里需要时用ExSceneView类,参考如下即可实现,暂不放效果截图了。
最初的需求
之前有实现视频背景、图片背景的需求,曾在sceneview的discussions中提过我的实现思路。(链接)
当时,我创建了一个ExSceneView的类,实现扩展背景的功能。(现已集成至Sceneform-EQR)
图片背景样例
- 获取Externaltexture
ExSceneView sceneView = (ExSceneView) scene.getView();
externalTexture = sceneView.getExternalTexture();
- 在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,那么我们就可以实现播放视频背景。
- 获取Externaltexture
ExSceneView sceneView = (ExSceneView) scene.getView();
backgroundTexture = sceneView.getExternalTexture();
- 配合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);
}
}