AR.js是一个web端的AR库,它完全开源免费,获得了很高的热度。我们要实现的效果如下:
首先去github下载AR.js库:
建议顺带看下作者给出的介绍。介绍里给出一个示例,我们在此示例的源码进行分析并尝试修改示例中的三维模型。
解压缩后目录如下:
示例存three.js目录的example目录下。只有在服务器环境内才可以访问。我们可以将AR.js拷贝至Apache服务器中访问,最简单的方法是:将AR.js用集成开发工具(webstorm,hbuilder等)打开,然后在开发环境中打开three.js/example,找到dev.html将其打开,点击运行即可。
我们会看到以下效果:
(由于是晚上拍的,可能效果不是很好)
我们将dev.html进行修改,改变显示的三维模型。
首先我们在example文件夹中创建一个class文件夹,存放我们自己写的实例。在class文件夹中新建一个Charactor.html文件。
路径如图:
在Charactor.html文件中编辑代码:
1.引入three.js库 及帧数查看组件
<script src='vendor/three.js/build/three.js'></script>
<script src='vendor/three.js/examples/js/libs/stats.min.js'></script>
three.js是前端实现三维显示的库,我们用它来创建的三维模型并添加页面(场景)中显示。
stats是帧数查看组件,它用来检测前端动画的运行状态。AR.js能够达到60帧以上,它足够优秀。
2.引入jsartoolkit
<script src='../vendor/jsartoolkit5/build/artoolkit.min.js'></script>
<script src='../vendor/jsartoolkit5/js/artoolkit.api.js'></script>
three.js的作用是实现三维显示,jsartoolkit则是实现摄像头的调用以及摄像头所获取影像的分析(AR的核心功能正是在此)。(此处注意一下,谷歌浏览器已经不允许http开头网址访问摄像头(本地调试除外),但火狐支持仍然支持,如果你是网站开发者,而又没有https的网址,可以推荐你的用户使用火狐浏览器。)
3.引入threex.artoolkit
<script src='../src/threex/threex-artoolkitsource.js'></script>
<script src='../src/threex/threex-artoolkitcontext.js'></script>
<script src='../src/threex/threex-artoolkitprofile.js'></script>
<script src='../src/threex/threex-arbasecontrols.js'></script>
<script src='../src/threex/threex-armarkercontrols.js'></script>
<script src='../src/threex/threex-arsmoothedcontrols.js'></script>
<script>THREEx.ArToolkitContext.baseURL = '../'</script>
three.js与jsartoolkit本来毫无关系,而threex.artoolkit将他们联系到一起,事实上,这才是AR.js库的精髓所在。
4.three.js要素初始化
//
// Init
//
// init renderer
var renderer = new THREE.WebGLRenderer({
// antialias : true,
alpha: true
});
renderer.setClearColor(new THREE.Color('lightgrey'), 0)
// renderer.setPixelRatio( 2 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.domElement.style.position = 'absolute'
renderer.domElement.style.top = '0px'
renderer.domElement.style.left = '0px'
document.body.appendChild( renderer.domElement );
// array of functions for the rendering loop
var onRenderFcts= [];
// init scene and camera
var scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x666666 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0x887766 );
directionalLight.position.set( -1, 1, 1 ).normalize();
scene.add( directionalLight );
这是基本的three.js的知识,如果你接触过three.js,那么你会发现这与three.js创建要素别无二致。
5.开启摄像头
//
// Initialize a basic camera
//
// Create a camera
var camera = new THREE.Camera();
scene.add(camera);
// handle arToolkitSource
var arToolkitSource = new THREEx.ArToolkitSource({
// to read from the webcam
sourceType : 'webcam',
// // to read from an image
// sourceType : 'image',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/images/img.jpg',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/images/armchair.jpg',
// to read from a video
// sourceType : 'video',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/videos/headtracking.mp4',
})
arToolkitSource.init(function onReady(){
onResize()
})
// handle resize
window.addEventListener('resize', function(){
onResize()
})
function onResize(){
arToolkitSource.onResizeElement()
arToolkitSource.copyElementSizeTo(renderer.domElement)
if( arToolkitContext.arController !== null ){
arToolkitSource.copyElementSizeTo(arToolkitContext.arController.canvas)
}
}
分析源码发现,此处还有其他识别模式,可以选择从照片或是从视频中识别要素。
6.初始化arToolKitContext
// initialize arToolkitContext
// create atToolkitContext
var arToolkitContext = new THREEx.ArToolkitContext({
cameraParametersUrl: THREEx.ArToolkitContext.baseURL + '../data/data/camera_para.dat',
// debug: true,
// detectionMode: 'mono_and_matrix',
detectionMode: 'mono',
// detectionMode: 'color_and_matrix',
// matrixCodeType: '3x3',
canvasWidth: 80*3,
canvasHeight: 60*3,
maxDetectionRate: 30,
})
// initialize it
arToolkitContext.init(function onCompleted(){
// copy projection matrix to camera
camera.projectionMatrix.copy( arToolkitContext.getProjectionMatrix() );
})
// update artoolkit on every frame
onRenderFcts.push(function(){
if( arToolkitSource.ready === false ) return
arToolkitContext.update( arToolkitSource.domElement )
})
这一步的作用是通过Canvas来联系摄像头与three.js,即在摄像界面上添加画板,以实现在摄像界面中作图的目的。
6.创建识别标记(ArMarker)
// Create a ArMarkerControls
var markerRoot = new THREE.Group
scene.add(markerRoot)
var markerControls = new THREEx.ArMarkerControls(arToolkitContext, markerRoot, {
// type: 'barcode',
// barcodeValue: 5,
type : 'pattern',
patternUrl : THREEx.ArToolkitContext.baseURL + 'examples/marker-training/examples/pattern-files/pattern-marker.patt',
})
// build a smoothedControls
var smoothedRoot = new THREE.Group()
scene.add(smoothedRoot)
var smoothedControls = new THREEx.ArSmoothedControls(smoothedRoot, {
lerpPosition: 0.4,
lerpQuaternion: 0.3,
lerpScale: 1,
// minVisibleDelay: 1,
// minUnvisibleDelay: 1,
})
onRenderFcts.push(function(delta){
smoothedControls.update(markerRoot)
})
// smoothedControls.addEventListener('becameVisible', function(){
// console.log('becameVisible event notified')
// })
// smoothedControls.addEventListener('becameUnVisible', function(){
// console.log('becameUnVisible event notified')
// })
此处我们用到的带黑框的图片标记是ArToolKit的第一代标记,之后发展到可以直接识别图片,如果有兴趣,可以到github上专门下载ArtoolKit。
7.将物体添加到场景
var arWorldRoot=smoothedRoot
var mesh=new THREE.AxisHelper();
//markerRoot.add(mesh)
arWorldRoot.add(mesh);
var loadingManager = new THREE.LoadingManager( function() {
arWorldRoot.add( elf );
} );
var loader = new THREE.ColladaLoader( loadingManager );
loader.load( '../examples/models/collada/elf/elf.dae', function ( collada ) {
elf = collada.scene;
} );
我们在此修改了添加到场景当中的模型,并添加到场中当中。
8.渲染整个three.js画面
//
// render the whole thing on the page
//
var stats = new Stats();
document.body.appendChild( stats.dom );
// render the scene
onRenderFcts.push(function(){
renderer.render( scene, camera );
stats.update();
})
// run the rendering loop
var lastTimeMsec= null
requestAnimationFrame(function animate(nowMsec){
// keep looping
requestAnimationFrame( animate );
// measure time
lastTimeMsec = lastTimeMsec || nowMsec-1000/60
var deltaMsec = Math.min(200, nowMsec - lastTimeMsec)
lastTimeMsec = nowMsec
// call each update function
onRenderFcts.forEach(function(onRenderFct){
onRenderFct(deltaMsec/1000, nowMsec/1000)
})
})
场景的动态效果都通过onRenderFct函数数组来实现。若要实现动画效果,我们要把实现动态的方法添加到数组中。
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Charactor</title>
<!-- three.js library -->
<script src='../examples/vendor/three.js/build/three.js'></script>
<script src='../examples/vendor/three.js/examples/js/libs/stats.min.js'></script>
<!-- jsartookit -->
<script src='../vendor/jsartoolkit5/build/artoolkit.min.js'></script>
<script src='../vendor/jsartoolkit5/js/artoolkit.api.js'></script>
<!-- include threex.artoolkit -->
<script src='../src/threex/threex-artoolkitsource.js'></script>
<script src='../src/threex/threex-artoolkitcontext.js'></script>
<script src='../src/threex/threex-artoolkitprofile.js'></script>
<script src='../src/threex/threex-arbasecontrols.js'></script>
<script src='../src/threex/threex-armarkercontrols.js'></script>
<script src='../src/threex/threex-arsmoothedcontrols.js'></script>
<script>THREEx.ArToolkitContext.baseURL = '../'</script>
</head>
<body>
<script src="../examples/js/loaders/ColladaLoader.js"></script>
<script src="../examples/js/controls/OrbitControls.js"></script>
<script src="../examples/js/Detector.js"></script>
<script src="../examples/js/libs/stats.min.js"></script>
<script>
//init renderer(初始化渲染器)
var renderer=new THREE.WebGLRenderer({
alpha:true
});
renderer.setClearColor(new THREE.Color('lightgrey'),0);
//renderer setPiexRatio(2);
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.domElement.style.position='absolute';
renderer.domElement.style.top='0px';
renderer.domElement.style.left='0px';
document.body.appendChild(renderer.domElement);
//array of functions for the rendering loop(渲染处理函数组初始化)
var onRenderFcts=[];
//init scene and camera
var scene=new THREE.Scene();//初始化场景和环境
var ambient=new THREE.AmbientLight(0x666666);
scene.add(ambient);
var directctionalLight=new THREE.DirectionalLight(0x887766);
directctionalLight.position.set(-1,1,1).normalize();
scene.add(directctionalLight);
//Initialize a basic camera
//Create a camera(初始化相机添加到场景)
var camera=new THREE.Camera();
scene.add(camera);
//handle arToolkitSource(调用打开相机事件,由THREEx提供)
var arToolkitSource=new THREEx.ArToolkitSource({
//to read from the webcam
sourceType:'webcam'
// // to read from an image
// sourceType : 'image',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/images/img.jpg',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/images/armchair.jpg',
// to read from a video
// sourceType : 'video',
// sourceUrl : THREEx.ArToolkitContext.baseURL + '../data/videos/headtracking.mp4',
})
arToolkitSource.init(function onReady() {
onResize();
})
//handle resize(处理重新调整大小后正常显示)
window.addEventListener('resize',function () {
onResize();
})
function onResize() {
arToolkitSource.onResizeElement()
arToolkitSource.copyElementSizeTo(renderer.domElement)
if(arToolkitContext.arController!==null){
arToolkitSource.copyElementSizeTo(arToolkitContext.arController.canvas);
}
}
//初始化 ArcToolkit环境, 相机内部场景
//initialize arToolkitContext
//create acToolkitContext
var arToolkitContext=new THREEx.ArToolkitContext({
//相机参数设置
cameraParametersUrl:THREEx.ArToolkitContext.baseURL+'../data/data/camera_para.dat',
//debug:true,
//detectionMode:'mono_and_matrix',
detectionMode:'mono',
// detectionMode:'color_and_matrix',
// matrixCodeType:'3x3',
canvasWidth:80*3,
canvasHeight:60*3,
maxDetectionRate:30, //最大旋转角度还是什么滴
})
//initialize it
arToolkitContext.init(function onCompleted() {
//copy projection matrix to camera
camera.projectionMatrix.copy(arToolkitContext.getProjectionMatrix());
});
//update artoolkit on every frame
onRenderFcts.push(function () {
if(arToolkitSource.ready==false) return;
arToolkitContext.update(arToolkitSource.domElement)
})
//Create a ArMakerControls
//创建一个Ar标记
var markerRoot=new THREE.Group(); //用threejs的点集合初始化。
scene.add(markerRoot);
var markerControls=new THREEx.ArMarkerControls(arToolkitContext,markerRoot,{
//type:'barcode',
//barcodeValue:5,
type:'pattern',
patternUrl:THREEx.ArToolkitContext.baseURL+'./examples/marker-training/examples/pattern-files/pattern-marker.patt',
})
//build a smoothedControls
var smoothedRoot=new THREE.Group();
scene.add(smoothedRoot);
var smoothedControls=new THREEx.ArSmoothedControls(smoothedRoot,{
lerpPosition:0.4,
lerpQuaternion:0.3,
lerpScale:1,
//minVisibleDaly:1,
//minUnvisibleDely:1,
})
onRenderFcts.push(function (delta) {
smoothedControls.update(markerRoot)
})
smoothedControls.addEventListener('becameVisible',function () {
console.log('becameVisible event notified')
})
//add Object in the scene
//添加物体
var arWorldRoot=smoothedRoot
var mesh=new THREE.AxisHelper();
//markerRoot.add(mesh)
arWorldRoot.add(mesh);
//add a torus knot创建物体
// collada
var loader = new THREE.ColladaLoader();
loader.load( '../examples/models/collada/stormtrooper/stormtrooper.dae', function ( collada ) {
var animations = collada.animations;
//调整对象状态
var avatar = collada.scene;
avatar.rotation.x=Math.PI;
avatar.rotation.z=Math.PI;
avatar.scale.set(0.5,0.5,0.5);
mixer = new THREE.AnimationMixer( avatar );
arWorldRoot.add( avatar );
var action = mixer.clipAction( animations[ 0 ] ).play();
onRenderFcts.push(function () {
avatar.rotation.z+=0.02*Math.PI;
})
} );
//renderer the Whole thing on the page
//渲染场景到页面中
//渲染率查看器
var stats=new Stats();
document.body.appendChild(stats.dom);
//renderer the scene
onRenderFcts.push(function () {
renderer.render(scene,camera);
stats.update();
})
//行程渲染事件环路
//run the rendering loop
var lastTimeMsec=null;
requestAnimationFrame(function animate(nowMsec){
//keep looping
requestAnimationFrame(animate);
//measure time
lastTimeMsec=lastTimeMsec||nowMsec-1000/60;
var deltaMsec=Math.min(200,nowMsec-lastTimeMsec)
//call all each update function
onRenderFcts.forEach(function (onRenderFct) {
onRenderFct(deltaMsec/1000,nowMsec/1000)
})
})
</script>
</body>
</html>
源码文件:
百度网盘:https://pan.baidu.com/s/1YTZAB6o12docx1JN-qopuw
密码:t6vp
Marker图片: