一、Three.js是什么?
Three.js是一个基于WebGL(Web Graphics Library)的3D图形渲染库,因为浏览器里最底层的3D API是WebGL。直接写会像写 OpenGL一样复杂,繁琐。Three.js 就是 WebGL 的封装层,提供了更高层次的接口,让你用几行代码就能创建出复杂的 3D 场景。
二、Three.js的核心概念
1.Scene(场景)
这是一个容器,一个无限大的虚拟空间。你之后创建的所有物体、光源、相机都必须被添加到这个场景中。
js
const scene = new THREE.Scene();
2.Camera(相机)
它决定了我们从哪个角度、以何种视野去观察这个场景。最常用的是透视相机 PerspectiveCamera,它模拟了人眼的视觉效果(近大远小)。
js
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
3.WebGLRenderer(渲染器)
渲染器会根据相机的位置和视野,将场景中的内容计算并绘制到一个HTML的元素上。
js
const renderer = new THREE.WebGLRenderer({ antialias: true }); // antialias开启抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement); // 将canvas添加到页面
4.OrbitControls(控制器)
控制器可以让用户用鼠标或触摸来旋转、缩放、平移相机。
js
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼效果,使旋转更平滑
controls.update();
5.Light光源
模拟自然光照、阴影、反光等效果
- 环境光 AmbientLight: 提供一种无方向的、均匀的基础光照,确保场景中最暗的地方也不是纯黑。
- 平行光 DirectionalLight: 模拟像太阳光一样的效果,从一个方向平行照射过来,可以产生阴影。
js
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 颜色, 强度
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7); // 设置光源位置
scene.add(directionalLight);
三、实战案例
核心分4大块:
1.Android原生:作为宿主,负责创建和管理WebView,处理原生UI交互,并准备3D模型文件(可以是本地,也可以是网络上获取的)。
2.WebView:负责加载本地的HTML和JavaScript文件。
3.JavaScript Bridge (@JavascriptInterface):这是原生代码与WebView中JavaScript代码双向通信的通道。
4.Assets:存放我们的index.html、Three.js库文件、模型文件以及核心的渲染逻辑main.js。
完整代码如下:
index.html主要是搭建一个网页环境,用于加载和执行main.js。
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Three.js on Android</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.158.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.158.0/examples/jsm/"
}
}
</script>
<script type="module" src="./main.js"></script>
</body>
</html>
main.js主要处理3D场景的创建、物体添加、动画循环等核心逻辑。
js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xeeeeee); // 设置背景色
//创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 75:场景范围
camera.position.set(0, 1, 5);
//创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement); // renderer.domElement 本质是canvas
//添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 环境光
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 平行光
directionalLight.position.set(5, 10, 7.5);
scene.add(directionalLight);
//添加控制器,允许用户用手势旋转、缩放模型
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼效果,使旋转更平滑
controls.update();
function getUrl() {
const urlString = androidBridge.getUrl()
const info = JSON.parse(androidBridge.getUrl())
return info.url
}
//加载 3D 模型 (GLTF/GLB)
const loader = new GLTFLoader();
loader.load(getUrl(),
function (gltf) {
const model = gltf.scene;
// 调整模型大小和位置
model.scale.set(5, 5, 5); // 调整模型初始的大小
model.position.set(0, 0, 0);
model.rotation.y += Math.PI; // 修改模型的 rotation属性 ,GLB 模型 180 度旋转
scene.add(model);
},
function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
androidBridge.sendDataToAndroid(xhr.loaded / xhr.total * 100);
},
function (error) {
console.error('An error happened', error);
}
);
// 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update(); // 更新控制器
renderer.render(scene, camera);
}
animate();
//处理窗口大小变化
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
AndroidBridge.kt主要是把从服务器获取的3d模型url传递到main.js中,然后再从main.js中获取加载进度回调到Android原生代码中。
js
class AndroidBridge {
private var info: String? = null
private var progressCallback: ProgressCallback? = null
// 定义回调接口
interface ProgressCallback {
fun onProgressUpdate(progress: Int)
}
// 设置回调
fun setProgressCallback(callback: ProgressCallback) {
this.progressCallback = callback
}
@JavascriptInterface
fun getUrl(): String {
return info.orEmpty()
}
fun setUrl(info: String?) {
this.info = info
}
@JavascriptInterface
fun sendDataToAndroid(progress: Int) {
progressCallback?.onProgressUpdate(progress)
}
}
MainActivity.kt主要是提供webview,以及从服务器获取3D模型url,完成和js的交互。
js
1.初始化WebView
val webSettings = settings
webSettings.javaScriptEnabled = true
webSettings.allowFileAccess = true
webSettings.allowFileAccessFromFileURLs = true
webSettings.allowContentAccess = true
webSettings.domStorageEnabled = true
webSettings.builtInZoomControls = false
webSettings.displayZoomControls = false
webSettings.allowUniversalAccessFromFileURLs = true
setWebChromeClient(object : WebChromeClient() {
override fun onConsoleMessage(cm: ConsoleMessage): Boolean {
Log.d("three", cm.message() + " at " + cm.sourceId() + ":" + cm.lineNumber())
return true
}
})
2.获取3D模型url
bindViewModel {
ai3Data.observe(this@WebViewActivity) {
if (!TextUtils.isEmpty(it.data?.glb_url)) {
bindView {
val glbUrl = it.data?.glb_url
val bridge = AndroidBridge()
webView.addJavascriptInterface(bridge,"androidBridge")
bridge.setUrl("{"url":"$glbUrl"}")
webView.loadUrl("file:///android_asset/threejs/index.html")
bridge.setProgressCallback(object :AndroidBridge.ProgressCallback {
override fun onProgressUpdate(progress: Int) {
runOnUiThread {
if (progress < 100) {
tvStateQuest.text = "glb文件加载进度$progress%"
} else {
tvStateQuest.text = "glb文件加载进度$progress%"
tvStateQuest.text = "glb文件加载结束"
}
}
}
})
}
}
}
}
以上代码就是利用WebView作为桥梁,驱动Three.js,在Android原生应用中渲染3D模型的例子。
四、Three.js还能做什么?
1.网页3D游戏
2.炫酷的动画特效
3.3D图表的数据可视化
4.电商网站的3D产品展示
5.房产APP中房屋的3D展示