zoukankan      html  css  js  c++  java
  • 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);
        }
    }
  • 相关阅读:
    350 Intersection of Two Arrays II 两个数组的交集 II
    349 Intersection of Two Arrays 两个数组的交集
    347 Top K Frequent Elements 前K个高频元素
    345 Reverse Vowels of a String 反转字符串中的元音字母
    344 Reverse String 反转字符串
    343 Integer Break 整数拆分
    342 Power of Four 4的幂
    338 Counting Bits Bit位计数
    Java常见面试题之Forward和Redirect的区别
    字节、字、bit、byte的关系
  • 原文地址:https://www.cnblogs.com/smedas/p/12467069.html
Copyright © 2011-2022 走看看