Threejs从入门到入门

前言
threejs官网:https://threejs.org/
github各个版本:https://github.com/mrdoob/three.js/tags
版本更迭很快,我用的时候还是r90秒秒钟r91出来了

刚入门的时候可以看看这个教程:http://www.hewebgl.com/article/articledir/1
初级教程是免费的,中高级是收费的,写的不是很枯燥,也不用跟着敲代码,有个相机,渲染器,场景,光源的概念就可以了。它适配的版本是比较旧的,我当时用新版本跟着写有些demo无法完成,新手更谈不上根据版本之间的差别找到问题,慢慢就失去兴趣了。

我的这个教程用的是r90版本,它有个比较好的地方是,当你使用高版本的threejs却使用低版本的语法时,浏览器console会有警告告诉你新的写法是什么样的。

我的DEMO下载
演示地址: 太穷了,带宽很小,加载卡顿请谅解(哭泣)
相机转动做的比较仓促没有考虑细节,导致鼠标拖拽或是放大缩小卡顿,emmm小问题暂时不想改了
https://www.hugeoyzy.top/threejs/src/demo1.html
https://www.hugeoyzy.top/threejs/src/demo2.html
下载:https://download.csdn.net/download/u010588262/10288776

开发工具
因为我主要是做java开发,所以就用idea了,因为idea写js也是有提示的,所以还挺方便,就建个Static Web项目就可以了: 
 

 我的项目结构是这样的: 
 

 调试需要做两点配置: 
1. 浏览器路径: 

  浏览器启动参数,因为threejs要加载本地的图片文件,obj文件之类的,看网上教程都说是不允许的,要部署在apache和tomcat之类的,我没试,看到说可以加启动参数就加了,调试没问题 

–allow-file-access-from-files 

  调试的时候就在html文件上鼠标移到右上角,点击相应的浏览器图标就可以了 

 

如果修改了html的话直接保存文件在浏览器刷新就能看到效果了,但是修改了js的话就要关闭页面重新点击浏览器图标。

官方DEMO(肥肠重要)
我入门最快的时候就是找到了官方DEMO之后

一个是:http://stemkoski.github.io/Three.js/#hello-world
这里的demo很入门,很好理解,页面最上方有github地址,可以去下载。唯一不好的是使用的版本很老。r60版本的,不过对于初学者一开始最重要的是理解大概的使用,不用纠结于最新版本。

还有一个是:https://threejs.org/examples/#webgl_animation_cloth
这里的demo相比第一个是比较高大上的,源代码就在你下载的threejs包里的examples目录下

代码讲解
这一块针对初学者中的初学者了,只要看过前言中的初级教程之后再看我的demo应该没问题了,demo的代码也比较整洁。

demo中的css,fonts,images和objs基本都是从官方demo中搞过来的,页面上引用的js分别是什么作用也做了注释了:
 

util.js是我把公用的东西放在一起了,要看懂util.js需要对threejs基本的组件有一些了解。

threejs的三围坐标是这样的,比如你现在对着电脑屏幕吧,把你屏幕的左下角想象成二维坐标原点,那往右就是x正方向,网上就是y正方向,从屏幕往外直盖脸的就是z正方向了。

最上面的src()是用来初始化场景,相机和渲染器的,最开始的textureLoader = new THREE.TextureLoader();是用来加载图片材质的,在大多数教程里用的都是ImageUtils来加载的,这个是新版本的改动,我说一下我对这仨玩意儿的理解吧

场景:就是一个黑盒子呗,把你要展示的东西全往里面装,地板啊,天花板啊,盒子啊,球啊,货架啊,三D字体啊全塞进去。为什么是黑盒子?因为你看不见它,也不看不见塞进去的东西。场景的初始化很简答就一句话scene = new THREE.Scene();

相机:相机可以在任意的位置,任意的角度”拍摄”黑盒子里的场景,相机的初始化稍微复杂一些。

    var SCREEN_WIDTH = window.innerWidth;
    var SCREEN_HEIGHT = window.innerHeight;
    var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    scene.add(camera);
    camera.position.set(0, 600, 1000);
    cameraRadius = Math.sqrt(camera.position.x * camera.position.x + camera.position.z * camera.position.z);
    camera.up.set(0, 1, 0);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

关于相机比较难理解的是position,up和lookAt的关系。你可看看这个视频http://www.hewebgl.com/video/2,虽然我看完了也没怎么看懂。
尝试解释一下吧。
现在,把你的手机打开摄像头,正对着屏幕,想象它就像单反一样上面右边有个快门,这就是threejs中相机的初始方向,就是从z轴正方向看向z轴负方向。position是什么意思呢,现在把手机往右平移,意思是position.x在增加。lookAt也很容易,就是相机往哪儿拍呗。比较难理解的是up,好现在把手机位置固定,只能旋转哦,也就是说position固定了,好了现在把手机旋转镜头对着屏幕左下角,也就是咱们的三维坐标原点,也就是说lookAt也固定了。up的作用就是你现在可以旋转手机了,保持镜头对着屏幕左下角的方向,手机的中心点位置不要变,旋转手机,拍出来的画面就会是正的,或者斜着的,或者上下倒过来的。随着你旋转手机快门的朝向也在变是吧,默认快门是对着Y轴正方向的,也就是(0,1,0)如果把手机颠倒,up就变成了(0,-1,0),拍出来的画面也就是上下颠倒的,因为你虽然可以倒着手机拍,但是看照片的时候得把手机正过来看,画面也就是反的咯。

渲染器
可以看到相机的初始化过程中没有与场景绑定,他们俩就像在两个平行世界一样,看似在一起,其实没在一起,需要用渲染器把他们俩关联起来,这样,相机就拍到了场景里的东西,再通过渲染器展示在浏览器上被我们看到了。Decetor是通过Detector.js引入进来的东西,用于判断浏览器兼容性的。

    if (Detector.webgl)
        renderer = new THREE.WebGLRenderer({antialias: true});
    else
        renderer = new THREE.CanvasRenderer();
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    container = document.getElementById('ThreeJS');
    container.appendChild(renderer.domElement);

以地板为例怎么搞一个东西放在场景里,CSDN的代码注释没有这么详细,写博客的时候加的,对不住只下资源不看文章的朋友。PlaneGeometry就是一个平面,SphereGeometry就是一个球,CubeGeometry就是一个立方体…….感兴趣的可以百度多了解一下其他组件。材质就是各种Material,也有很多种,有受光照影响的,有不受光照影响的。把组件和材质组合起来就是一个完整的物体啦,就是THREE.Mesh这个方法把他们组合起来,第一个参数是组件,第二个参数是材质,材质还可以传入一个数组,你可能觉得设置多个有啥意义啊一个盖住一个的,材质是可以设置透明度的,明白了吧。

function addfloor() {
    // 加载图片作为地板材质
    var floorTexture = textureLoader.load('images/checkerboard.jpg');
    // 沿x方向和Y方向都重复填充
    floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
    // x方向和y方向各重复几次,不理解的话改改这个值看看效果就知道了
    floorTexture.repeat.set(4, 4);
    // 将材质包装成表面材料,设置正反两面都要铺上
    var floorMaterial = new THREE.MeshBasicMaterial({map: floorTexture, side: THREE.DoubleSide});
    // 平面对象
    var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1);
    // 把对象和材料包装成Mesh
    floor = new THREE.Mesh(floorGeometry, floorMaterial);
    // 设置位置放入场景啦
    floor.position.y = -0.5;
    floor.rotation.x = Math.PI / 2;
    scene.add(floor);
}

others()方法里是一些可有可无的辅助,看注释就明白了。
animate()就是开始持续渲染,渲染就是通过renderer.render(scene, camera);把渲染器搞到的东西放在浏览器上给你看,持续渲染就是通过重复调用方法本身可以实现动画的效果requestAnimationFrame(animate);,除了这两个东西还调用了update();方法,update方法里就是慢慢的移动组件或者刷新其他需要更新得组件。requestAnimationFrame方法是比较平滑的动画,我之前想用Tween.js实现相机转动的效果,很卡顿,不流畅。

woodycube()就是封装了获得木箱,因为两个demo里都用到了木头箱子。

后面有空再说一下demo中的代码

2018年3月16日
更新了一下util.js中的代码,把鼠标点击选中事件和getMeshed方法从demo2中移到util里面了
点击事件

document.addEventListener("mousedown", onDocumentMouseDown, false);
function onDocumentMouseDown(e) {
    e.preventDefault();
    // 点击页面时停止相机旋转,因为相机旋转的时候无法选中物体,这个问题没有解决,有点麻烦
    rotate = false;
    //将鼠标点击位置的屏幕坐标转成threejs中的标准坐标,具体解释见代码释义
    var mouse = {};
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    //新建一个三维单位向量 假设z方向就是0.5
    //根据照相机,把这个向量转换到视点坐标系
    var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
    var sub = vector.sub(camera.position);=
    var param2 = sub.normalize();
    var raycaster = new THREE.Raycaster(camera.position, param2);

    //射线和模型求交,选中一系列直线,getMeshs方法用于获得场景中可以被选中的所有Mesh
    var intersects = raycaster.intersectObjects(getMeshs());

    if (intersects.length > 0) {
        // outlineMesh是物体边框组件,每次只会有一个物体被选中,就把这个东西定义为全局的
        // 每次选中另一个物体之前先删除,相当于取消前一个物体的选中
        scene.remove(outlineMesh);
        //选中第一个射线相交的物体
        var SELECTED = intersects[0].object;
        var outlineMaterial2 = new THREE.MeshBasicMaterial({color: 0x00ff00, side: THREE.BackSide});
        outlineMesh = new THREE.Mesh(SELECTED.geometry.clone(), outlineMaterial2);
        outlineMesh.position.set(SELECTED.position.x, SELECTED.position.y, SELECTED.position.z);
        outlineMesh.scale.multiplyScalar(1.05);
        scene.add(outlineMesh);
    }
}

 

posted on 2020-03-12 08:55  dawn888  阅读(3229)  评论(1编辑  收藏  举报

导航