赞
踩
本文的案例基于上一篇,【ThreeJS基础教程】1.4.1 更好的视觉效果-使用材质与灯光的最终案例源码做修改,以下会详细说明相对于之前的案例修改了哪些内容并做解析
在上一个案例中,修改init函数中的这一部分
light = new THREE.PointLight(0xffffff,0.7);
light.position.y += 5;
scene.add(light);
修改为
light = new THREE.PointLight(0xffffff,0.7);
camera.add(light);
scene.add(camera);
修改后的案例效果
修改后,当我们旋转视角时,会发现,不论旋转到什么角度,方块面向屏幕的一侧,会一直保持一个被照亮的状态
关于 camera.add(light)
当相机中添加了光源后,相机无论移动到何处,光源都会始终在相机上,相当于,你开着一个带着手电筒的摄像机,在拍摄东西
但是,仅仅把灯光加进相机中,相机不在场景中的话,灯光是无法照亮场景的
所以我们还需要把相机丢进场景中
这种技巧一般用于【单件商品展示型】项目,比如说:车辆展示,3D商品展示等
有个同学问了,为什么不能把灯光同时添加到相机和场景中呢?
threejs规定,一个Object3D对象,只能有一个父对象
这里的Object3D包含了其所有的继承类
访问父子对象的方法:
属性名 | 属性类型 | 默认值 | 属性说明 |
---|---|---|---|
object3d.parent | Object3D | null | 这个属性可以用于访问父对象 |
object3d.children | [Object3D] | []空数组 | 这个属性可以用于访问其下的所有子对象 |
还有一个同学问了,为什么添加进相机后,灯光就跟着相机走了
这里涉及到一个东西叫相对坐标系
相对坐标系是指,一个物体相对于另一个物体位置而创造的坐标系的坐标系位置
比如说,我这个物体在(10,10,10)的位置,但是我如果以这个物体为核心,创造一个相对坐标系,那么,这个物体的所在位置,就为相对(0,0,0),此时,把灯光添加到这个物体的子类中时,我们的灯光的位置依然是(0,0,0),但是这里的坐标,已经是【相对这个物体的相对坐标系的(0,0,0)】,我们的绝对坐标系位置,在(10,10,10)
在threejs中,如果父级的位置发生变化后,其下的所有子级的物体,也会跟随父级而改变,但是所有的物体,仅绝对坐标系发生了改变,而相对坐标系依然为(0,0,0)
官方提供了4个相关函数,用于获取物体在scene中的绝对坐标
通过getWorldPosition这个函数,我们可以拿到物体在scene中实际的位置
注意,这个函数在最新版本中,要求你必须传入一个THREE.Vector3()类型的对象,函数会将获取到的结果赋值到这个THREE.Vector3()的对象中,并且将这个结果以返回值的方式返回
我们可以这样写
let worldPosition = mesh.getWorldPosition(new THREE.Vector3());
我们也可以这样写
let vec3 = new THREE.Vector3();
mesh.getWorldPosition( vec3 );
两种方法,都可以获取到这个物体的相对位置
其他的三个函数在后续使用到之后,再做讲解
我们之前所有的场景,都是一个光秃秃的方块,什么都没有,一点也不好看,所以这次我们添加一个地面
function addMesh(){ let geometry = new THREE.BoxGeometry(1,1,1); let material = new THREE.MeshStandardMaterial({ color:"#ffffff" }); mesh = new THREE.Mesh(geometry,material); scene.add(mesh); } function addPlane(){ let geometry = new THREE.PlaneGeometry(10,10).rotateX(-Math.PI/2); let material = new THREE.MeshStandardMaterial({ color:"#8b8b8b" }); let plane = new THREE.Mesh(geometry,material); scene.add(plane); }
我们在addMesh()的下方,复制了一套代码, 并将其中的BoxGeometry()更换为PlaneGeometry,同时修改颜色为灰色“#8b8b8b”
哎呀?方块怎么陷入地下了?那不行,我们再对plane进行一次移动
function addPlane(){
let geometry = new THREE.PlaneGeometry(10,10).rotateX(-Math.PI/2).translate(0,-1,0);
let material = new THREE.MeshStandardMaterial({
color:"#8b8b8b"
});
let plane = new THREE.Mesh(geometry,material);
scene.add(plane);
}
这样我们的方块就在地面上跳舞了
如果你希望这个地面无限大,我们只需要设置好geometry的初始大小即可
function addPlane(){
let geometry = new THREE.PlaneGeometry(1000,1000).rotateX(-Math.PI/2).translate(0,-1,0);
let material = new THREE.MeshStandardMaterial({
color:"#8b8b8b"
});
let plane = new THREE.Mesh(geometry,material);
scene.add(plane);
}
最终效果
构造器:PlaneGeometry(width : Float, height : Float, widthSegments : Integer, heightSegments : Integer)
属性名 | 属性值类型 | 默认值 | 说明 |
---|---|---|---|
width | Float | 1 | 平面长度 |
height | Float | 1 | 平面宽度 |
widthSegments | Integer | 1 | 平面在长度方向的分段数,分段数越高,该平面更精细,这个值在后续在Geometry详解的文章中会详细介绍 |
heightSegments | Integer | 1 | 平面在宽度方向的分段数,同上 |
PlaneGeometry官方文档
BufferGeometry官方文档
函数:BufferGeometry.rotateX( angle:Float )
在 X 轴上旋转几何体。该操作一般在一次处理中完成,不会循环处理
其实我们还可以用
plane.rotation.x = -Math.PI 来代替上述用法,两种方法的不同点是:
当你访问plane.rotation时,使用BufferGeometry.rotateX的方法创建的平面,它的rotation值为(0,0,0),而使用上述方法时,rotation的值为(-3.14,0,0)
这两种方法都可以旋转物体,根据实际情况进行操作即可
个人建议,频繁操作某个物体的rotation时,优先修改Object3D.rotation的值,而非使用BufferGeometry.rotateX()函数
函数:translate( x : Float, y : Float, z : Float )
移动几何体,该操作一般在一次处理中完成,不会循环处理
上面我们对plane的y的值,设置为-1,这时创建的plane就是低于地面的
在前面的文章中,我们把geometry比做演员,Mesh比做化妆后的演员
对geometry进行操作,本质上,是修改了演员的基本素质,比如说本次修改了plane的旋转角度和位置,相当于,我们找了一个比正常的演员更矮一点的演员
而对Mesh进行操作,更多的是调整演员在演出时的状态
同样是修改位置,如果我们选择高个子的演员,那么我们需要降低高个子所占的舞台,来确保演员站在了合适的位置,这个属于修改mesh来实现,而操作geometry则是选择了矮个子的演员,而没有降低我们的舞台
操作两者均可达到移动几何体的目的,但是要分清楚情况去使用
地面我们有了,但是场上只有一个演员,我希望让场景更丰富一些
function addMesh(){
for(let i = 0;i< 50;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:Math.random() * 0xffffff
});
let mesh = new THREE.Mesh(geometry,material);
mesh.position.x = Math.random() * 50 - 25;
mesh.position.z = Math.random() * 50 - 25;
scene.add(mesh);
}
}
修改addMesh()为上述代码后
我们的场景变的更加丰富多彩了
前面的文章有提到,在材质中,我们可以使用 16位进制数来表示颜色,忘了16位进制的同学可以先去复习一下
这里我们使用 0xffffff * Math.random 来生成随机颜色
同时,我们使用 Math.random * 50 - 25,生成一个随机数,随机范围为 -25~25,用于控制每个创建的物体的x轴和z轴,如果你需要让模型的高度也有不同的话,对y轴做同样的操作即可
但是,上面这种写法并不推荐
当你循环了50次,你就创建了50个geometry,50个material,50个mesh,直接创建了150个对象,这样你的内存中就有了50个对象的内存
这就相当于,我们这场舞台上,有50个规格完全一样的演员,50套衣服,和50个化妆后的演员
其实我们完全可以只用一个演员,50套衣服,然后让一个演员去做50个演员的工作,这样我们会节省很多的内存
function addMesh(){
let geometry = new THREE.BoxGeometry(1,1,1);
for(let i = 0;i< 50;i++){
let material = new THREE.MeshStandardMaterial({
color:Math.random() * 0xffffff
});
let mesh = new THREE.Mesh(geometry,material);
mesh.position.x = Math.random() * 50 - 25;
mesh.position.z = Math.random() * 50 - 25;
scene.add(mesh);
}
}
这样做,我们就只创建了一个演员,50套衣服,50个不同状态的化妆后的演员,我们这样就可以节省下来49个演员(BufferGeometry)所占用的内存
在创建Mesh的时候,Geometry是可以多次使用的,Material也可以多次使用,所以利用这个特性,我们可以对大量重复的物体做这样的优化
比如说,你要做一个森林,如果你把每一棵树的细节都做出来,每棵树都做的完全不一样,那么你这个程序就别想在任何设备上跑起来了。。。
像我们玩过的很多游戏中,树木这种东西都是复用的,比如说,我只需要5棵完全不同的树,然后将它复制几百份,随机分布到整个场景中,这样我们的森林就出来了
这样做,不只是节约了你做模型的成本,也大幅节省了运行你的程序时,所消耗的内存
嗯?你说你想要尽量让用户不看到重复的树?
那简单,我们还能调整树的旋转方向,或者拉伸树木模型来增加不同的感觉
如果你还觉得不够,那么,我们可以把树的种类增加到10棵。。。
在多次调整后,你总能找到一个,既保证了最低的内存,也保证了最满足需求的一个平衡点
function addMesh(){
let geometry = new THREE.BoxGeometry(1,1,1);
for(let i = 0;i< 100;i++){
let material = new THREE.MeshStandardMaterial({
color:Math.random() * 0xffffff
});
let mesh = new THREE.Mesh(geometry,material);
// i/10获取到第几排,乘1.1拉开一点距离
mesh.position.x = Number.parseInt(i /10) * 1.1;
// 以10对i求余获取到第几列
mesh.position.z = ( i % 10) * 1.1;
scene.add(mesh);
}
}
function addMesh(){
let geometry = new THREE.BoxGeometry(1,1,1);
for(let i = 0;i< 100;i++){
let material = new THREE.MeshStandardMaterial({
color:Math.random() * 0xffffff
});
let mesh = new THREE.Mesh(geometry,material);
//典型sin函数
mesh.position.x = i * 1.1;
mesh.position.z = Math.sin(i) * 2;
scene.add(mesh);
}
}
function addMesh(){
let geometry = new THREE.BoxGeometry(1,1,1);
for(let i = 0;i< 100;i++){
let material = new THREE.MeshStandardMaterial({
color:Math.random() * 0xffffff
});
let mesh = new THREE.Mesh(geometry,material);
//螺旋线
mesh.position.y = i ;
mesh.position.z = Math.sin(i) * 4;
mesh.position.x = Math.cos(i) * 4;
scene.add(mesh);
}
}
这方面的玩法有很多很多,这里仅拿几个简单的举例,感兴趣的同学可以研究研究相关的函数,看看你们可以做出多少种效果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。