使用ThreeJs从零开始构建3D智能仓库——第三章(选中物体与特效)

写在前面——目录结构

这一章我们来完成激动人心的,关于如何鼠标单击选中一个物体,并让该物体周围闪烁白光,并在鼠标点击位置的上方显示该物体的名字。如下图所示:
2019.11.26 更新:我最近建立了个人网站,大家可以访问下面的链接查看演示
3D仓库演示
2019.11.28 更新:代码和图片资源等已上传至GitHub
https://github.com/xiao149/ThreeJsDemo
在这里插入图片描述
在开始这章内容的讲解之前,我想先给大家推荐下项目的目录结构,因为这一章我们将要引入自己写的一个JS文件ThreeJs_Composer.js,该JS的作用就是添加选中特效和显示物体的名字等等一系列功能。我们这个3D仓库未来将会用到很多不同的功能,全部写进主页面的HTML会导致代码过长,可阅读性下降。所以我推荐大家将不同的功能封装成不同的JS(比如选中的效果、拖动物体、显示提示信息、加载模型等等),需要用到的时候再导入这些JS就会方便易懂很多。
我的目录结构如下:
在这里插入图片描述
稍微介绍下,layui是一个开源的UI框架,最近我正在研究,前台向来是我这种初学者的痛啊!现在还用不到,大家略过。ThreeJs放着我们需要用的一切,images存放图片,js存放ThreeJs原生的JS,pace是第三方的加载控件,这里也不用管,剩下的是我们自己创建的JS和主页面test.html,今天我们主要来研究ThreeJs_Composer.js这个文件。

如何选中物体

选中物体的原理

鉴于我也是从各种百度到的地方了解到这些内容的,只能说是知其然而不知其所以然,再次我就仅仅描述我所知道的东西,若有所纰漏还请原谅~~
选中一个物体我们需要依靠Raycaster这个工具,其实质是在你鼠标所指的地方(如下图)发射一条射线,对我们可以想象一条直线,垂直你的电脑屏幕,在你鼠标的位置,从屏幕外直直指向屏幕内,系统将会获取到该射线依次经过的物体。
在这里插入图片描述
举个栗子,我们可以在浏览器中按下F12打开调试模式,我这时点击了一个窗户,进入断点在我红色箭头所指地方的intersects便保存了射线依次经过的物体,分别是窗户、地面、地面。
在这里插入图片描述
在这里插入图片描述
换一个比较刁钻的角度,这次我点击了最左侧的门,由下图可见射线返回了两个物体,分别是左门1和左边的那个墙面:
在这里插入图片描述

选中物体的实现

经过上面的描述,我想大家对如何点击选中一个物体有了初步的了解,现在我们可以来创建ThreeJs_Composer这个自定义的JS了:

/*
  * 需要在jsp中导入的包
  <script src="./ThreeJs/three.js"></script>
  <script src="./ThreeJs/EffectComposer.js"></script>
  <script src="./ThreeJs/RenderPass.js"></script>
  <script src="./ThreeJs/OutlinePass.js"></script>
  <script src="./ThreeJs/FXAAShader.js"></script>
  <script src="./ThreeJs/ShaderPass.js"></script>
  <script src="./ThreeJs/CopyShader.js"></script>
  */

THREE.ThreeJs_Composer = function ( _renderer, _scene, _camera) {
    var raycaster = new THREE.Raycaster();
  	var mouse = new THREE.Vector2();
    var selectedObjects = [];

    window.addEventListener( 'click', onMouseClick);
    
    function onMouseClick( event ) {
        var x, y;
        if ( event.changedTouches ) {
            x = event.changedTouches[ 0 ].pageX;
            y = event.changedTouches[ 0 ].pageY;
        } else {
            x = event.clientX;
            y = event.clientY;
        }
        mouse.x = ( x / window.innerWidth ) * 2 - 1;
        mouse.y = - ( y / window.innerHeight ) * 2 + 1;
        raycaster.setFromCamera( mouse, _camera );
        var intersects = raycaster.intersectObjects( [ _scene ], true );

        if(intersects.length == 0){
            $("#label").attr("style","display:none;");//隐藏说明性标签
            return;
        }
        if(intersects[0].object.name == "地面" || (intersects[0].object.name == "") || (intersects[0].object.name == "墙面")){
            $("#label").attr("style","display:none;");//隐藏说明性标签
            selectedObjects.pop();
        }else{
            $("#label").attr("style","display:block;");// 显示说明性标签
            $("#label").css({left: x, top: y-40});// 修改标签的位置
            $("#label").text(intersects[0].object.name);// 显示模型信息

            selectedObjects.pop();
            selectedObjects.push( intersects[0].object );
        }
    }
}

我来简单介绍下,首先需要传入的三个参数_renderer, _scene, _camera分别是HTML中创建好的渲染器,场景和相机,我们需要在test.html中导入这个JS就像这样

<script src="./ThreeJs/ThreeJs_Composer.js"></script>

然后在init()方法中加入(这里的test.html依旧是第二章所讲的那个,忘记的同学可以回到第二章回顾下)

//添加选中时的蒙版
new THREE.ThreeJs_Composer(renderer, scene, camera);

回到我们创建的JS中,下面这个变量已经保存了我们鼠标点击处发出射线所依次经过的物体:

var intersects = raycaster.intersectObjects( [ _scene ], true );

接下来我们会有三种情况:
1.鼠标点击的地方啥都没有,就直接隐藏我们的说明性标签(就是显示在鼠标上面的那个框),直接return。

if(intersects.length == 0){
    $("#label").attr("style","display:none;");//隐藏说明性标签
    return;
}

2.点击到了地面或者墙面,这种情况下我们一般不显示说明,并且把selectedObjects这个数组清空,这个数组将会存放我们选中的物体,方便之后添加发光特效。

if(intersects[0].object.name == "地面" || (intersects[0].object.name == "") || (intersects[0].object.name == "墙面")){
    $("#label").attr("style","display:none;");//隐藏说明性标签
    selectedObjects.pop();
}

3.点击到了门窗或者其他能够选中的物体,这种情况下显示说明(物体的名字),并且把selectedObjects这个数组清空后赋值。

else{
    $("#label").attr("style","display:block;");// 显示说明性标签
    $("#label").css({left: x, top: y-40});// 修改标签的位置
    $("#label").text(intersects[0].object.name);// 显示模型信息

    selectedObjects.pop();
    selectedObjects.push( intersects[0].object );
}

完成后效果图如下(鼠标点击了左门1):
在这里插入图片描述

添加选中后的发光特效

这个需要我们用到ThreeJs的后期处理THREE.EffectComposer,以及一系列“通道”,THREE.RenderPassTHREE.OutlinePassTHREE.ShaderPass
我们在之前的自定义JS中修改如下:

/*
  * 需要在jsp中导入的包
  <script src="./ThreeJs/three.js"></script>
  <script src="./ThreeJs/EffectComposer.js"></script>
  <script src="./ThreeJs/RenderPass.js"></script>
  <script src="./ThreeJs/OutlinePass.js"></script>
  <script src="./ThreeJs/FXAAShader.js"></script>
  <script src="./ThreeJs/ShaderPass.js"></script>
  <script src="./ThreeJs/CopyShader.js"></script>
  */

THREE.ThreeJs_Composer = function ( _renderer, _scene, _camera) {
    var raycaster = new THREE.Raycaster();
  	var mouse = new THREE.Vector2();
    var composer = new THREE.EffectComposer( _renderer );
    var renderPass = new THREE.RenderPass( _scene, _camera );
    var selectedObjects = [];
    composer.addPass( renderPass );
    var outlinePass = new THREE.OutlinePass( new THREE.Vector2( window.innerWidth, window.innerHeight ), _scene, _camera );
    outlinePass.edgeStrength = 5;//包围线浓度
    outlinePass.edgeGlow = 0.5;//边缘线范围
    outlinePass.edgeThickness = 2;//边缘线浓度
    outlinePass.pulsePeriod = 2;//包围线闪烁频率
    outlinePass.visibleEdgeColor.set( '#ffffff' );//包围线颜色
    outlinePass.hiddenEdgeColor.set( '#190a05' );//被遮挡的边界线颜色
    composer.addPass( outlinePass );
    var effectFXAA = new THREE.ShaderPass( THREE.FXAAShader );
    effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
    effectFXAA.renderToScreen = true;
    composer.addPass( effectFXAA );

    window.addEventListener( 'click', onMouseClick);

    function onMouseClick( event ) {
        var x, y;
        if ( event.changedTouches ) {
            x = event.changedTouches[ 0 ].pageX;
            y = event.changedTouches[ 0 ].pageY;
        } else {
            x = event.clientX;
            y = event.clientY;
        }
        mouse.x = ( x / window.innerWidth ) * 2 - 1;
        mouse.y = - ( y / window.innerHeight ) * 2 + 1;
        raycaster.setFromCamera( mouse, _camera );
        var intersects = raycaster.intersectObjects( [ _scene ], true );

        if(intersects.length == 0){
            $("#label").attr("style","display:none;");//隐藏说明性标签
            return;
        }
        if(intersects[0].object.name == "地面" || (intersects[0].object.name == "") || (intersects[0].object.name == "墙面")){
            $("#label").attr("style","display:none;");//隐藏说明性标签
            selectedObjects.pop();
        }else{
            $("#label").attr("style","display:block;");// 显示说明性标签
            $("#label").css({left: x, top: y-40});// 修改标签的位置
            $("#label").text(intersects[0].object.name);// 显示模型信息

            selectedObjects.pop();
            selectedObjects.push( intersects[0].object );
            outlinePass.selectedObjects = selectedObjects;//给选中的线条和物体加发光特效
        }
    }

    return composer;
}

关于后期通道这方面我了解的很少,有兴趣的同学可以自行百度,官网上也有很多栗子,比如雪花特效啊,变暗变亮的特效啊等等。这里修改后的JS和之前章节创建的差不多,白色光圈的特效主要依靠OutlinePass通道,我们将射线获取到的selectedObjects赋给outlinePass就可以实现文章开头演示的效果啦!其中下面的这些参数大家可以依据自己的喜好适度修改。

outlinePass.edgeStrength = 5;//包围线浓度
outlinePass.edgeGlow = 0.5;//边缘线范围
outlinePass.edgeThickness = 2;//边缘线浓度
outlinePass.pulsePeriod = 2;//包围线闪烁频率
outlinePass.visibleEdgeColor.set( '#ffffff' );//包围线颜色
outlinePass.hiddenEdgeColor.set( '#190a05' );//被遮挡的边界线颜色

HTML更新如下

<!DOCTYPE html>
<html>

<head includeDefault="true">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <title>3D库图显示</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

    #label {
      position: absolute;
      padding: 10px;
      background: rgba(255, 255, 255, 0.6);
      line-height: 1;
      border-radius: 5px;
    }
  </style>
  <script src="./ThreeJs/js/three.js"></script>
  <script src="./ThreeJs/js/stats.min.js"></script>
  <script src="./ThreeJs/js/DragControls.js"></script>
  <script src="./ThreeJs/js/OrbitControls.js"></script>
  <script src="./ThreeJs/js/dat.gui.min.js"></script>
  <script src="./ThreeJs/js/EffectComposer.js"></script>
  <script src="./ThreeJs/js/RenderPass.js"></script>
  <script src="./ThreeJs/js/OutlinePass.js"></script>
  <script src="./ThreeJs/js/FXAAShader.js"></script>
  <script src="./ThreeJs/js/CopyShader.js"></script>
  <script src="./ThreeJs/js/ShaderPass.js"></script>
  <script src="./ThreeJs/js/OBJLoader.js"></script>
  <script src="./ThreeJs/js/MTLLoader.js"></script>
  <script src="./ThreeJs/js/ThreeBSP.js"></script>
  <script src="./ThreeJs/ThreeJs_Composer.js"></script>
  <script src="./ThreeJs/js/jquery-1.11.0.min.js"></script>
</head>

<body>
  <div id="label"></div>
  <div id="container"></div>
  <script>
    var stats = initStats();
    var scene, camera, renderer, controls, light, composer;
    var matArrayA = []; //内墙
    var matArrayB = []; //外墙
    var group = new THREE.Group();

    // 初始化场景
    function initScene() {
      scene = new THREE.Scene();
      scene.fog = new THREE.Fog(scene.background, 3000, 5000);
    }

    // 初始化相机
    function initCamera() {
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      camera.position.set(0, 800, 1500);
      camera.lookAt(new THREE.Vector3(0, 0, 0));
    }

    // 初始化灯光
    function initLight() {
      var directionalLight = new THREE.DirectionalLight(0xffffff, 0.3); //模拟远处类似太阳的光源
      directionalLight.color.setHSL(0.1, 1, 0.95);
      directionalLight.position.set(0, 200, 0).normalize();
      scene.add(directionalLight);

      var ambient = new THREE.AmbientLight(0xffffff, 1); //AmbientLight,影响整个场景的光源
      ambient.position.set(0, 0, 0);
      scene.add(ambient);
    }

    // 初始化性能插件
    function initStats() {
      var stats = new Stats();

      stats.domElement.style.position = 'absolute';
      stats.domElement.style.left = '0px';
      stats.domElement.style.top = '0px';

      document.body.appendChild(stats.domElement);
      return stats;
    }

    // 初始化渲染器
    function initRenderer() {
      renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setClearColor(0x4682B4, 1.0);
      document.body.appendChild(renderer.domElement);
    }

    //创建地板
    function createFloor() {
      var loader = new THREE.TextureLoader();
      loader.load("./ThreeJs/images/floor.jpg", function(texture) {
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set(10, 10);
        var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
        var floorMaterial = new THREE.MeshBasicMaterial({
          map: texture,
          side: THREE.DoubleSide
        });
        var floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.position.y = -0.5;
        floor.rotation.x = Math.PI / 2;
        floor.name = "地面";
        scene.add(floor);
      });
    }

    //创建墙
    function createCubeWall(width, height, depth, angle, material, x, y, z, name) {
      var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
      var cube = new THREE.Mesh(cubeGeometry, material);
      cube.position.x = x;
      cube.position.y = y;
      cube.position.z = z;
      cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
      cube.name = name;
      scene.add(cube);
    }

    //创建门_左侧
    function createDoor_left(width, height, depth, angle, x, y, z, name) {
      var loader = new THREE.TextureLoader();
      loader.load("./ThreeJs/images/door_left.png", function(texture) {
        var doorgeometry = new THREE.BoxGeometry(width, height, depth);
        doorgeometry.translate(50, 0, 0);
        var doormaterial = new THREE.MeshBasicMaterial({
          map: texture,
          color: 0xffffff
        });
        doormaterial.opacity = 1.0;
        doormaterial.transparent = true;
        var door = new THREE.Mesh(doorgeometry, doormaterial);
        door.position.set(x, y, z);
        door.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
        door.name = name;
        scene.add(door);
      });
    }

    //创建门_右侧
    function createDoor_right(width, height, depth, angle, x, y, z, name) {
      var loader = new THREE.TextureLoader();
      loader.load("./ThreeJs/images/door_right.png", function(texture) {
        var doorgeometry = new THREE.BoxGeometry(width, height, depth);
        doorgeometry.translate(-50, 0, 0);
        var doormaterial = new THREE.MeshBasicMaterial({
          map: texture,
          color: 0xffffff
        });
        doormaterial.opacity = 1.0;
        doormaterial.transparent = true;
        var door = new THREE.Mesh(doorgeometry, doormaterial);
        door.position.set(x, y, z);
        door.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
        door.name = name;
        scene.add(door);
      });
    }

    //创建窗户
    function createWindow(width, height, depth, angle, x, y, z, name) {
      var loader = new THREE.TextureLoader();
      loader.load("./ThreeJs/images/window.png", function(texture) {
        var windowgeometry = new THREE.BoxGeometry(width, height, depth);
        var windowmaterial = new THREE.MeshBasicMaterial({
          map: texture,
          color: 0xffffff
        });
        windowmaterial.opacity = 1.0;
        windowmaterial.transparent = true;
        var window = new THREE.Mesh(windowgeometry, windowmaterial);
        window.position.set(x, y, z);
        window.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
        window.name = name;
        scene.add(window);
      });
    }

    //返回墙对象
    function returnWallObject(width, height, depth, angle, material, x, y, z, name) {
      var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
      var cube = new THREE.Mesh(cubeGeometry, material);
      cube.position.x = x;
      cube.position.y = y;
      cube.position.z = z;
      cube.rotation.y += angle * Math.PI;
      cube.name = name;
      return cube;
    }

    //墙上挖门,通过两个几何体生成BSP对象
    function createResultBsp(bsp, objects_cube) {
      var material = new THREE.MeshPhongMaterial({
        color: 0x9cb2d1,
        specular: 0x9cb2d1,
        shininess: 30,
        transparent: true,
        opacity: 1
      });
      var BSP = new ThreeBSP(bsp);
      for (var i = 0; i < objects_cube.length; i++) {
        var less_bsp = new ThreeBSP(objects_cube[i]);
        BSP = BSP.subtract(less_bsp);
      }
      var result = BSP.toMesh(material);
      result.material.flatshading = THREE.FlatShading;
      result.geometry.computeFaceNormals(); //重新计算几何体侧面法向量
      result.geometry.computeVertexNormals();
      result.material.needsUpdate = true; //更新纹理
      result.geometry.buffersNeedUpdate = true;
      result.geometry.uvsNeedUpdate = true;
      scene.add(result);
    }

    //创建墙纹理
    function createWallMaterail(){
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //前  0xafc0ca :灰色
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //后
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //上  0xd6e4ec: 偏白色
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //下
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //左    0xafc0ca :灰色
        matArrayA.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //右

        matArrayB.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //前  0xafc0ca :灰色
        matArrayB.push(new THREE.MeshPhongMaterial({color: 0x9cb2d1}));  //后  0x9cb2d1:淡紫
        matArrayB.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //上  0xd6e4ec: 偏白色
        matArrayB.push(new THREE.MeshPhongMaterial({color: 0xd6e4ec}));  //下
        matArrayB.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //左   0xafc0ca :灰色
        matArrayB.push(new THREE.MeshPhongMaterial({color: 0xafc0ca}));  //右
    }
    
    // 初始化模型
    function initContent() {
      createFloor();
      createWallMaterail();
      createCubeWall(10, 200, 1400, 0, matArrayB, -1295, 100, 0, "墙面");
      createCubeWall(10, 200, 1400, 1, matArrayB, 1295, 100, 0, "墙面");
      createCubeWall(10, 200, 2600, 1.5, matArrayB, 0, 100, -700, "墙面");
      //创建挖了门的墙
      var wall = returnWallObject(2600, 200, 10, 0, matArrayB, 0, 100, 700, "墙面");
      var door_cube1 = returnWallObject(200, 180, 10, 0, matArrayB, -600, 90, 700, "前门1");
      var door_cube2 = returnWallObject(200, 180, 10, 0, matArrayB, 600, 90, 700, "前门2");
      var window_cube1 = returnWallObject(100, 100, 10, 0, matArrayB, -900, 90, 700, "窗户1");
      var window_cube2 = returnWallObject(100, 100, 10, 0, matArrayB, 900, 90, 700, "窗户2");
      var window_cube3 = returnWallObject(100, 100, 10, 0, matArrayB, -200, 90, 700, "窗户3");
      var window_cube4 = returnWallObject(100, 100, 10, 0, matArrayB, 200, 90, 700, "窗户4");
      var objects_cube = [];
      objects_cube.push(door_cube1);
      objects_cube.push(door_cube2);
      objects_cube.push(window_cube1);
      objects_cube.push(window_cube2);
      objects_cube.push(window_cube3);
      objects_cube.push(window_cube4);
      createResultBsp(wall, objects_cube);
      //为墙面安装门
      createDoor_left(100, 180, 2, 0, -700, 90, 700, "左门1");
      createDoor_right(100, 180, 2, 0, -500, 90, 700, "右门1");
      createDoor_left(100, 180, 2, 0, 500, 90, 700, "左门2");
      createDoor_right(100, 180, 2, 0, 700, 90, 700, "右门2");
      //为墙面安装窗户
      createWindow(100, 100, 2, 0, -900, 90, 700, "窗户");
      createWindow(100, 100, 2, 0, 900, 90, 700, "窗户");
      createWindow(100, 100, 2, 0, -200, 90, 700, "窗户");
      createWindow(100, 100, 2, 0, 200, 90, 700, "窗户");
    }

    // 初始化轨迹球控件
    function initControls() {
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.5;
      // 视角最小距离
      controls.minDistance = 100;
      // 视角最远距离
      controls.maxDistance = 5000;
      // 最大角度
      controls.maxPolarAngle = Math.PI / 2.2;
    }

    // 更新控件
    function update() {
      stats.update();
      controls.update();
    }

    // 初始化
    function init() {
      initScene();
      initCamera();
      initRenderer();
      initContent();
      initLight();
      initControls();

      //添加选中时的蒙版
      composer = new THREE.ThreeJs_Composer(renderer, scene, camera);
      document.addEventListener('resize', onWindowResize, false);
    }

    // 窗口变动触发的方法
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function animate() {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
      composer.render();
      update();
    }

    init();
    animate();
  </script>
</body>

</html>

结束语

码着码着就到十一点半了,最近项目快上线了比较忙,工作时间基本都在处理项目上的琐事,也只有牺牲下晚上玩游戏的时间来更新文章了(滑稽),本章我们讲解了如何选中一个物体并添加特效。下一章我们将会推出如何给门添加打开关闭的动画,以及如何添加库位,敬请期待!
我跟广大学习ThreeJs的初学者一样,仍带着懵懂的心去探索这片新大陆,CSDN上的许多前辈都给了我很多关键的灵感和技术方法,如果大家有兴趣,也可以互相交流成长,欢迎大家指导咨询。PS:大家有兴趣可以点进去我的头像,陆陆续续也写了十来篇了。
链接:使用ThreeJs从零开始构建3D智能仓库——第一章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第二章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第三章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第四章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第五章: 点我跳转.

对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。 此教学版本为threeJS107版本。 关于版本不建议大家使用低于90的版本学习。 以下是课程目录 1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景) 2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等) 3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理) 4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等) 5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用) 6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作) 7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解) 8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道) 9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动) 10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库) 11-常见渲染以及透明度问题 12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果) 13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势) 14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动) 15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法) 16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页