2.1更好的视觉效果-综合案例(2
学习ThreeJS的捷径
本段内容会写在0篇以外所有的,本人所编写的Threejs教程中
对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久
如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS
- 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
- 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
- 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
https://www.wellyyss.cn/ 跃焱邵隼
http://www.wjceo.com/ 暮志未晚
这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处
初识纹理Texture
先提前纠正一下纹理(Texture)和贴图(Map)不是一个东西
纹理一般是指贴图贴在模型表面之后,显示出来的样子,称为纹理,通常会由贴图来生成纹理
贴图的本质就是一张图片资源
如何生成一片草地
let geometry = new THREE.PlaneGeometry(10,10,10).rotateX(-Math.PI/2);
let material = new THREE.MeshBasicMaterial({
color:"#00ff00"
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
上述方式虽然可以制作一块绿色的平面,但是这很难让人联想到这里是一块草地
这时候我们就需要借助纹理来生成草地了
function addMesh(){
let texture = new THREE.TextureLoader().load("./grass.jpg");
let geometry = new THREE.PlaneGeometry(10,10,10).rotateX(-Math.PI/2);
let material = new THREE.MeshBasicMaterial({
map:texture
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
}
这样的草地是否显得更逼真一些呢?
Texture与TextureLoader
先在这里说明一下,官方的写法这里存在问题
上面的案例也是按照官方写法而写的,但是这种写法会存在一个问题,就是TextureLoader.load()是一个异步函数,当你的图片加载速度低于你使用纹理的速度时,就有可能会出现错误,尤其是当你要一次加载多个纹理时,或者你加载的贴图过于巨大时(极高分辨率的大型巨型图片),此时受网速影响加载纹理太慢时,就会出现这样的问题
这种错误一般是线上更容易发生,而在本地开发过程中几乎不会遇到
因此笔者建议从一开始接触纹理时,就使用下面的写法
function addMesh(){
//创建纹理加载器
let textureLoader = new THREE.TextureLoader();
//加载器读取grass.jpg,加载完成时执行下面的函数
textureLoader.load("./grass.jpg",(texture)=>{
let geometry = new THREE.PlaneGeometry(10,10,10).rotateX(-Math.PI/2);
let material = new THREE.MeshBasicMaterial({
map:texture
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
});
}
官方文档中的wrapS和warpT等暂时不在本篇讨论,后续将有一篇文章单独对纹理详细讲解
Texture官方文档
TextureLoader官方文档
关于Loader会在后续专门写一篇文章来详细讲解,本篇仅对纹理做基本介绍
函数:textureLoader.load(url : String, onLoad : Function, onProgress : Function, onError : Function );
url:图片路径
onLoad:资源全部加载完成时的回调函数
onProgress:在加载过程中调用,用于监控加载进度
onError:加载错误时回调
在前期的学习和开发中,笔者建议始终添加onError参数来监控加载情况,后续讲到模型加载时会再提这方面事宜
let textureLoader = new THREE.TextureLoader();
textureLoader.load("./gr2ass.jpg",(texture)=>{
let geometry = new THREE.PlaneGeometry(10,10,10).rotateX(-Math.PI/2);
let material = new THREE.MeshBasicMaterial({
map:texture
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
},null,(error)=>{
console.log(error);
});
如果仅对一个文件做加载时,onProgress函数可以先写为null
在onLoad函数中,我们可以获取到加载完成的资源,textureLoader中,已经将生成好的纹理,以对象的方式返回给我们了,所以这里我们直接访问它即可
官方写法中,并没有使用()=>{}这样的箭头函数,这种写法容易造成this指向错误,所以这里笔者建议,从这里开始,每一个Load函数,都使用箭头函数,以免出现一些奇奇怪怪的问题
箭头函数为ES6的语法,不熟悉ES6的同学这时还可以去了解一下
当我们拿到了加载完成后的纹理后,将它作为值,传给Material的map属性
let material = new THREE.MeshBasicMaterial({
map:texture
});
如果这里再添加Color属性的话,Color会覆盖当前加载出来的纹理颜色
这样我们的草地就被创建出来了
案例完整源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas{
display: block;
}
body {
margin: 0;
overscroll-behavior: none;
}
#btns{
position: absolute;
top:10%;
width: 500px;
height: 100px;
left: 50%;
transform:translateX(-50%);
}
</style>
</head>
<body>
<div id="btns"></div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../../three.js-master/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from '../../three.js-master/build/three.module.js';
import {OrbitControls} from "../../three.js-master/examples/jsm/controls/OrbitControls.js";
let scene,renderer,camera,orbitControls;
function init(){
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
alpha:true,
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
camera.position.set(10,10,10);
orbitControls = new OrbitControls(camera,renderer.domElement);
scene.add(new THREE.AmbientLight(0xffffff,1));
let helper = new THREE.AxesHelper(5);
scene.add(helper);
}
function addMesh(){
let textureLoader = new THREE.TextureLoader();
textureLoader.load("./grass.jpg",(texture)=>{
let geometry = new THREE.PlaneGeometry(10,10,10).rotateX(-Math.PI/2);
let material = new THREE.MeshBasicMaterial({
map:texture
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
},null,(error)=>{
console.log(error);
});
}
function render(){
renderer.render(scene,camera);
requestAnimationFrame(render);
}
init();
addMesh();
render();
</script>
</body>
</html>
案例使用的图片文件,图片来源:three.js-master\examples\textures\terrain下的草地贴图
做一个简单的地球出来
上面我们学会了怎么使用纹理,那么,这次我们不再将纹理贴到plane上,贴到我们的球体Sphere上
地球的贴图素材three.js-master\examples\textures\planets,这里我们选用的素材是earth_atmos_2048.jpg
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
canvas{
display: block;
}
body {
margin: 0;
overscroll-behavior: none;
}
#btns{
position: absolute;
top:10%;
width: 500px;
height: 100px;
left: 50%;
transform:translateX(-50%);
}
</style>
</head>
<body>
<div id="btns"></div>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../../three.js-master/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from '../../three.js-master/build/three.module.js';
import {OrbitControls} from "../../three.js-master/examples/jsm/controls/OrbitControls.js";
let scene,renderer,camera,orbitControls;
function init(){
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
camera.position.set(10,10,10);
orbitControls = new OrbitControls(camera,renderer.domElement);
let helper = new THREE.AxesHelper(5);
scene.add(helper);
let light = new THREE.PointLight();
camera.add(light);
scene.add(camera);
}
function addMesh(){
let textureLoader = new THREE.TextureLoader();
textureLoader.load("./earth_atmos_2048.jpg",(texture)=>{
let geometry = new THREE.SphereGeometry(5,32,32);
let material = new THREE.MeshStandardMaterial({
map:texture
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
},null,(error)=>{
console.log(error);
});
}
function render(){
renderer.render(scene,camera);
requestAnimationFrame(render);
}
init();
addMesh();
render();
</script>
</body>
</html>
完整效果:
大多数知识点在前面已经解析完成,笔者在这里就不多做解释了
下一篇预告:加载模型
下一篇主要讲解主流的几种模型格式与Loader,以及加载管理器LoadingManager