项目中需要渲染3D模型,最初采用的是google的filament,好用是好用,但是有3个痛点:一是会出现kotlin版本不兼容问题,二是apk大小陡增了17.5M,所以考虑换一种实现方式。
而Three.js+原生WebView就完美解决了上述痛点,一是不需要依赖第三方库没有版本兼容问题,二是只需要引入少量js文件、一个html文件、极少量java代码,apk大小只增加了750k。
以下就是具体实现方式:
在assets中放置4个文件,three.min.js(核心库)、GLTFLoader.js(根据自己的模型文件类型选择)、OrbitControls.js(手势控制,放缩旋转之类)、index.html。
js库也可以通过cdn引入,那样会更加精简,但是这里必须使用133版本,否则有些代码不兼容。
js下载地址(防止cdn失效):【免费】v133:three.min.js+GLTFLoader.js+OrbitControls.js资源-优快云文库
index.html内容为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js 3D Model</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- 引入 Three.js -->
<!--<script src="https://cdn.jsdelivr.net/npm/three@0.133.0/build/three.min.js"></script>-->
<!--<script src="https://cdn.jsdelivr.net/npm/three@0.133.0/examples/js/loaders/GLTFLoader.js"></script>-->
<!--<script src="https://cdn.jsdelivr.net/npm/three@0.133.0/examples/js/controls/OrbitControls.js"></script>-->
<script src="three.min.js"></script>
<script src="GLTFLoader.js"></script>
<script src="OrbitControls.js"></script>
<script>
// 从 URL 参数中获取模型名称
const urlParams = new URLSearchParams(window.location.search);
const modelName = urlParams.get('model');
// 场景、相机、渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({
antialias: true, // 启用抗锯齿
powerPreference: 'high-performance' // 启用高性能模式
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio); // 使用设备的像素比例提高清晰度
document.body.appendChild(renderer.domElement);
// 添加环境光源,参数2为亮度值
const ambientLight = new THREE.AmbientLight(0xffffff, 8);
scene.add(ambientLight);
// 加载 3D 模型
const loader = new THREE.GLTFLoader();
let model;
loader.load(modelName, function (gltf) {
model = gltf.scene;
scene.add(model);
// 计算模型的包围盒
const box = new THREE.Box3().setFromObject(model);
const size = new THREE.Vector3();
box.getSize(size);
// 计算模型的中心点
const center = new THREE.Vector3();
box.getCenter(center);
model.position.sub(center);
// 计算相机的位置
const maxSize = Math.max(size.x, size.y, size.z);
const fov = camera.fov * (Math.PI / 180);
const distance = maxSize / (2 * Math.tan(fov / 2))*2;// 最后*2是为了缩小模型
camera.position.set(0, 0, distance);
}, undefined, function (error) {
console.error('Error loading model:', error);
});
// 初始化 OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
在Activity中对WebView简单配置一下即可(可以根据自己需求对WebView进行优化):
private WebView myWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_render);
myWebView = findViewById(R.id.webview);
myWebView.getSettings().setJavaScriptEnabled(true);
String model = "pole.glb";
myWebView.loadUrl("file:///android_asset/index.html?model="+model); // 加载本地 HTML 文件
}
记得在destroy中销毁WebView,否则重新加载url时会概率性白屏:
@Override
protected void onDestroy() {
myWebView.destroy();
super.onDestroy();
}