Sceneform-EQR是EQ基于sceneform(filament)扩展的一个用于安卓端的三维渲染器。
Sceneform-EQR
示例地址:Sceneform-EQR中BaseSceneActivity.java
安卓布局文件:
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)。
参考代码提交日志:
由于性能,改回使用surfaceView
这里需要时用ExSceneView类,参考如下即可实现,暂不放效果截图了。
之前有实现视频背景、图片背景的需求,曾在sceneview的discussions中提过我的实现思路。(链接)
当时,我创建了一个ExSceneView的类,实现扩展背景的功能。(现已集成至Sceneform-EQR)
ExSceneView sceneView = (ExSceneView) scene.getView();
externalTexture = sceneView.getExternalTexture();
如下,那么同理可以在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,那么我们就可以实现播放视频背景。
ExSceneView sceneView = (ExSceneView) scene.getView();
backgroundTexture = sceneView.getExternalTexture();
关键: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);
}
}