>上一部分的three.js教程还是很久前写的,因为种种原因一直没有更新了(放心,除了这次的教程,还至少会有一篇的,大部分代码我是早就写好测试过了的,更新上来只是迟早的事,以后可能还会对前面几篇做一些调整,一是解决一些没讲清的地方,顺便加入几张图片便于。
好了,看看我们上次讲到哪了,先回顾下上次的代码吧,如果我没弄错,现在文件基本上是这样了:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>webgl互交测试</title>
- <style>
- *{
- padding: 0;
- margin: 0;
- }
- #b{
- width: 200px;
- height: 500px;
- background: #000;
- }
- </style>
- </head>
- <body>
- <div id="b">
- <div id="canvas-frame"></div>
- </div>
- <script src="three-js/build/three.js" data-ke-src="https://raw.github.com/mrdoob/three.js/master/build/three.js"></script>
- <script src="three-js/examples/js/libs/stats.min.js"></script>
- <script>
- function mythree(id){
- this.dom=document.getElementById(id);
- this.dom.style.width=window.innerWidth+"px";
- this.dom.style.height=window.innerHeight+"px";
- this.scene = new THREE.Scene();//场景
- this.camera=new THREE.PerspectiveCamera(75, this.dom.offsetWidth / this.dom.offsetHeight, 0.1, 1000);
- this.camera.position.set(0,200,0);
- this.camera.lookAt(this.scene.position);
- this.renderer = new THREE.WebGLRenderer();//渲染器
- this.renderer.setSize(this.dom.offsetWidth,this.dom.offsetHeight);//设置渲染器大小
- this.renderer.setClearColorHex(0x000000, 1);//设置渲染器颜色
- this.dom.appendChild(this.renderer.domElement);
- this.stats = new Stats();
- this.stats.domElement.style.position = 'absolute';
- this.stats.domElement.style.left = '0px';
- this.stats.domElement.style.top = '0px';
- this.dom.appendChild(this.stats.domElement);
- this.rander=(function (obj){
- return function(){
- obj.renderer.render(obj.scene, obj.camera);//渲染
- requestAnimationFrame(obj.rander);
- obj.stats.update();
- }
- })(this);
- this.rander();
- }
- var mt=new mythree('b');
- var light = new THREE.DirectionalLight(0xffffff,1.0,0); //设置平行光源
- light.position.set(200,100,200); //设置光源向量
- mt.scene.add(light); //追加光源到场景
- var c=new THREE.Mesh(
- new THREE.CubeGeometry(40,40,40),
- new THREE.MeshLambertMaterial({color: 0x0088ff})
- );//创建实物
- c.position.set(0,0,0);
- mt.scene.add(c);
- window.onkeydown=function(ev){
- //alert(ev.keyCode);
- switch(ev.keyCode){
- case 65:
- mt.camera.position.z++;
- break;
- case 68:
- mt.camera.position.z--;
- break;
- case 87:
- mt.camera.position.x--;
- break;
- case 83:
- mt.camera.position.x++;
- break;
- }
- }
- </script>
- </body>
- </html>
接下来应该是通过键盘控制摄像机移动了吧,那我们接下来试试让它接受鼠标的控制改变视角(控制参照某些游戏,比如mc)。
首先我们需要弄清楚怎样控制摄像头的旋转:在three.js中,很多对象都有rotation属性,他是一个用于控制旋转对象,下面有x、y、z三个方向的旋转。不过在动工前,首先弄清一个关于它旋转方式的问题要少走很多的弯路。当是我没弄清这个,写了一段更复杂的js,然后发现效果不对(我的脖子怎么断了啊喂)。弄清它这个旋转是以自身坐标系的轴为中心旋转,并且这个坐标系会随着前面设定的参数而旋转,并不是一个固定的坐标系。
明白了它的旋转方式,我们应该知道了不同轴的旋转顺序会影响旋转效果,可以参考这张图:
画的丑了点,轻喷(昨天画这个耽误了好多时间)。
然后我们来看看我们应该怎么做吧:首先,左右移动鼠标这个“摄像头”也应该向左或向右看,那么就是绕着y轴旋转;上下移动鼠标则应该是上下看的效果,这似乎X轴和Z轴的旋转都会要,但是仔细想想之前说到了两个关键问题,首先,这个旋转是按照一定顺序依次绕三个轴旋转,并且整个坐标系也会跟着转,那么的话,如果我们让Y轴的转动最先转(实际上如果先转动了其他的轴左右转的部分也会需要调整)的话,上下看就只有X方向需要调整了。(当然如果要实现的话,办法还是很多,的我这里取了一种比较简单的方法,建议自己仔细思考下这个旋转的问题,前面几次写出来发现控制变得很迷也没有什么关系,你自己那个长方体模拟一下看看发生了什么会有一些帮助,另外默认的旋转顺序是先X轴再Y轴最后Z轴)。
接下来我们就该看看怎么样改变旋转顺序:我们可以发现一个叫eulerOrder的属性,它就是用来设置旋转顺序的。我们按如下方法实现:
- mt.camera.eulerOrder='YXZ';
接下里就是实现鼠标的控制,我们首先定义一个变量,它将用于存放上一次鼠标指针的位置,然后监听鼠标onmousemove事件,计算出鼠标移动的方向和距离然后然后让把这个值加到rotation上去就好了,当然在此之前你需要自己是;试一下它究竟是顺时针旋转还是逆时针旋转,这个不是很难,就留给大家自己试一试了,我这里也给出了一个示例代码片段:
- list=null;
- mt.dom.onmousemove=function(ev){
- if(!ev.x) ev.x=ev.layerX;//兼容火狐等浏览器
- if(!ev.y) ev.y=ev.layerY;
- if(list){
- mt.camera.rotation.y-=(ev.x-list.x)*0.01;
- mt.camera.rotation.x-=(ev.y-list.y)*0.01;
- }
- list={x:ev.x,y:ev.y};
- }
为了更好的检测我们的代码,我们在场景中增加一些东西之类的调整这样更能看词效果来(我可以比较轻松的移动到一个合适的角度,当然方向控制现在还有问题,因为我们的前后左右都改变了):
附上我的最终代码(注意有些地方调整了,虽然没有在教程中说我改过,不过用到的对象和函数应该都是讲过的,就不多说了。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>webgl互交测试2</title>
- <style>
- *{
- padding: 0;
- margin: 0;
- }
- #b{
- width: 200px;
- height: 500px;
- background: #000;
- }
- </style>
- </head>
- <body>
- <div id="b">
- <div id="canvas-frame"></div>
- </div>
- <script src="three-js/build/three.js" data-ke-src="https://raw.github.com/mrdoob/three.js/master/build/three.js"></script>
- <script src="three-js/examples/js/libs/stats.min.js"></script>
- <script>
- function mythree(id){
- this.dom=document.getElementById(id);
- this.dom.style.width=window.innerWidth+"px";
- this.dom.style.height=window.innerHeight+"px";
- this.scene = new THREE.Scene();//场景
- this.camera=new THREE.PerspectiveCamera(75, this.dom.offsetWidth / this.dom.offsetHeight, 0.1, 1000);
- this.camera.position.set(0,200,0);
- //this.camera.lookAt(this.scene.position);
- this.renderer = new THREE.WebGLRenderer();//渲染器
- this.renderer.setSize(this.dom.offsetWidth,this.dom.offsetHeight);//设置渲染器大小
- this.renderer.setClearColorHex(0x000000, 1);//设置渲染器颜色
- this.dom.appendChild(this.renderer.domElement);
- //document.body.appendChild(this.renderer.domElement);
- //this.camera.position.z = 180;//设置相机位置
- this.stats = new Stats();
- this.stats.domElement.style.position = 'absolute';
- this.stats.domElement.style.left = '0px';
- this.stats.domElement.style.top = '0px';
- this.dom.appendChild(this.stats.domElement);
- this.rander=(function (obj){
- return function(){
- obj.renderer.render(obj.scene, obj.camera);//渲染
- requestAnimationFrame(obj.rander);
- obj.stats.update();
- }
- })(this);
- this.rander();
- /*this.renderer.domElement.onmousemove=function(ev){
- alert(4)
- }*/
- }
- var mt=new mythree('b');
- var light = new THREE.DirectionalLight(0xffffff,1.0,0); //设置平行光源
- light.position.set(200,100,150); //设置光源向量
- mt.scene.add(light); //追加光源到场景
- var ca=new THREE.Mesh(
- new THREE.CubeGeometry(40,100,40),
- new THREE.MeshLambertMaterial({color: 0x0088ff})
- );//创建实物
- ca.position.set(0,0,0);
- mt.scene.add(ca);
- c=new THREE.Mesh(
- new THREE.CubeGeometry(1,400,400),
- new THREE.MeshLambertMaterial({color: 0x0088ff})
- );//创建实物
- c.position.set(-200,200,0);
- mt.scene.add(c);
- c=new THREE.Mesh(
- new THREE.CubeGeometry(400,1,400),
- new THREE.MeshLambertMaterial({color: 0x0088ff})
- );//创建实物
- c.position.set(0,-1,0);
- mt.scene.add(c);
- c=new THREE.Mesh(
- new THREE.CubeGeometry(400,400,1),
- new THREE.MeshLambertMaterial({color: 0x0088ff})
- );//创建实物
- c.position.set(0,200,-200);
- mt.scene.add(c);
- window.onkeydown=function(ev){
- //alert(ev.keyCode);
- switch(ev.keyCode){
- case 65:
- mt.camera.position.x--//=Math.sin();
- break;
- case 68:
- mt.camera.position.x++;
- break;
- case 87:
- mt.camera.position.z--;
- break;
- case 83:
- mt.camera.position.z++;
- break;
- }
- }
- list=null;
- mt.camera.eulerOrder='YXZ';//修改旋转顺序 默认为xyz
- //这一步可以使得绕x轴旋转时参考已经旋转的y轴的量(相当于让x轴也随y轴旋转了),能减少后续工作
- //不做这一步还会出现很多问题
- mt.dom.onmousemove=function(ev){
- if(!ev.x) ev.x=ev.layerX;
- if(!ev.y) ev.y=ev.layerY;
- if(list){
- mt.camera.rotation.y-=(ev.x-list.x)*0.01;
- mt.camera.rotation.x-=(ev.y-list.y)*0.01;
- }
- list={x:ev.x,y:ev.y};
- }
- </script>
- </body>
- </html>