12.Three.js纹理动画与动效墙案例
在Three.js的数字孪生场景应用中,我们通常会使用到一些动画渲染效果,如动效墙,飞线、雷达等等,今天主要了解一下其中一种动画渲染效果:纹理动画。下面实现以下动效墙效果(警戒墙动画)。根据该案例的实现思路还可以实现很多种动画效果。
1.纹理贴图和纹理动画
首先了解一下Three.js纹理贴图相关的类 Texture,构造函数如下:
Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace )
该api用于是一个纹理贴图对象,可以将其应用到一个表面,或者作为反射/折射贴图。
一般通过TextureLoader方式加载和创建:
const texture = new THREE.TextureLoader().load( "textures/water.jpg" );
texture.wrapS = THREE.RepeatWrapping; //水平方向重复包裹
texture.wrapT = THREE.RepeatWrapping; //垂直方向重复包裹
texture.repeat.set( 4, 4 );
参数说明:
wrapS:这个值定义了纹理贴图在水平方向上将如何包裹
wrapT:这个值定义了纹理贴图在垂直方向上将如何包裹
方法说明:
texture.repeat.set( 4, 4 ): 设定在水平、垂直方向上的重复次数
在我们给网格模型设置材质的时候,除了可以设置通用的基础材质或者兰伯特材质,还可以加上我们的纹理贴图,为网格模型套上”皮肤“
let texture;
function initObject() {
const geometry = new THREE.PlaneGeometry(600, 100 );
texture = new THREE.TextureLoader().load( "./img/warning.png" );
texture.repeat.set( 5, 1 );
// 设置.wrapS也就是U方向,纹理映射模式(包裹模式)
texture.wrapS = THREE.RepeatWrapping;//对应offste.x偏移
// 设置.wrapT也就是V方向,纹理映射模式
texture.wrapT = THREE.RepeatWrapping;//对应offste.y偏移
let material = new THREE.MeshBasicMaterial({
map: texture,
color: "#FFFFFF",
side: THREE.DoubleSide,
transparent:true
});
let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
mesh.rotateX(Math.PI/2);
scene.add(mesh);
}
2.纹理对象.offset
属性
纹理对象Texture的.offset
的功能是偏移贴图在Mesh上位置,本质上相当于修改了UV顶点坐标。
texture.offset.x +=0.5;//纹理U方向偏移
texture.offset.y +=0.5;//纹理V方向偏移
该属性结合渲染循环,动态的修改uv坐标就可以实现uv动画了
// 渲染循环
function render() {
texture.offset.x +=0.001;//设置纹理动画:偏移量根据纹理和动画需要,设置合适的值
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
3.基于平面几何体的动效墙效果代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Three框架</title>
<script src="../three.js-master/build/three.js"></script>
<script src="../three.js-master/examples/js/controls/OrbitControls.js"></script>
<script src="../three.js-master/examples/js/loaders/GLTFLoader.js"></script>
<script src="../three.js-master/examples/js/loaders/OBJLoader.js"></script>
<script src="../three.js-master/examples/js/loaders/MTLLoader.js"></script>
<style type="text/css">
body {
margin: 0;
padding: 0;
overflow-y: hidden;
overflow-x: hidden;
}
div#webgl {
width: 100vw;
height: 100vh;
}
</style>
<script>
var renderer;
function initThree() {
let dom = document.getElementById("webgl");
width = dom.clientWidth;
height = dom.clientHeight;
renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(width, height);
dom.appendChild(renderer.domElement);
renderer.setClearColor("#ffffff", 1.0);
}
var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
// camera = new THREE.OrthographicCamera( width/-2, width/2, height/2, height/-2, 1, 10000 );
camera.position.x = 0;
camera.position.y = 1000;
camera.position.z = 0;
camera.up.x = 0;
camera.up.y = 0;
camera.up.z = 1;
camera.lookAt({
x: 0,
y: 0,
z: 0,
});
}
var scene;
function initScene() {
scene = new THREE.Scene();
}
var light;
function initLight() {
light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(100, 100, 200);
scene.add(light);
}
let texture;
function initObject() {
const geometry = new THREE.PlaneGeometry(600, 100 );
texture = new THREE.TextureLoader().load( "./img/warning.png" );
texture.repeat.set( 5, 1 );
// 设置.wrapS也就是U方向,纹理映射模式(包裹模式)
texture.wrapS = THREE.RepeatWrapping;//对应offste.x偏移
// 设置.wrapT也就是V方向,纹理映射模式
texture.wrapT = THREE.RepeatWrapping;//对应offste.y偏移
let material = new THREE.MeshBasicMaterial({
map: texture,
color: "#FFFFFF",
side: THREE.DoubleSide,
transparent:true
});
let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
mesh.rotateX(Math.PI/2);
scene.add(mesh);
}
function threeStart() {
initThree(); //初始化Three.js渲染器等初始操作
initCamera(); //初始化相机
initScene(); //初始化场景
initLight(); //初始化灯光
initControls(); //初始化控制器
initObject(); //初始化渲染物体
initAxesHelper();
render(); //执行渲染
}
function initAxesHelper() {
// AxesHelper:辅助观察的坐标系(红x、绿y、蓝z)
const axesHelper = new THREE.AxesHelper(1500);
scene.add(axesHelper);
}
function initControls() {
// 设置相机控件轨道控制器OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener("change", function () {
renderer.render(scene, camera); //执行渲染操作
}); //监听鼠标、键盘事件
}
function render() {
// texture.offset.x -=0.01;//设置纹理动画:偏移量根据纹理和动画需要,设置合适的值
renderer.render(scene, camera);
requestAnimationFrame(render);
}
</script>
</head>
<body onload="threeStart();">
<div id="webgl"></div>
</body>
</html>
4.通过坐标的自定义几何体创建的动效墙效果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Three框架</title>
<script src="../three.js-master/build/three.js"></script>
<script src="../three.js-master/examples/js/controls/OrbitControls.js"></script>
<script src="../three.js-master/examples/js/loaders/GLTFLoader.js"></script>
<script src="../three.js-master/examples/js/loaders/OBJLoader.js"></script>
<script src="../three.js-master/examples/js/loaders/MTLLoader.js"></script>
<style type="text/css">
body {
margin: 0;
padding: 0;
overflow-y: hidden;
overflow-x: hidden;
}
div#webgl {
width: 100vw;
height: 100vh;
}
</style>
<script>
var renderer;
function initThree() {
let dom = document.getElementById("webgl");
width = dom.clientWidth;
height = dom.clientHeight;
renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(width, height);
dom.appendChild(renderer.domElement);
renderer.setClearColor("#ffffff", 1.0);
}
var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
// camera = new THREE.OrthographicCamera( width/-2, width/2, height/2, height/-2, 1, 10000 );
camera.position.x = 0;
camera.position.y = 1000;
camera.position.z = 0;
camera.up.x = 0;
camera.up.y = 0;
camera.up.z = 1;
camera.lookAt({
x: 0,
y: 0,
z: 0,
});
}
var scene;
function initScene() {
scene = new THREE.Scene();
}
var light;
function initLight() {
light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(100, 100, 200);
scene.add(light);
}
let texture;
function initObject() {
let c = [
100,100, //第一个点
-100,100, //第二个点
-100,-100,
100,-100,
100,100
]
let posArr = [];
let uvrr = [];
let h = 50; //围墙拉伸高度
for (let i = 0; i < c.length - 2; i += 2) {
// 围墙多边形上两个点构成一个直线扫描出来一个高度为h的矩形
// 矩形的三角形1
posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
// 矩形的三角形2
posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
// 注意顺序问题,和顶点位置坐标对应
uvrr.push(0, 0, 1, 0, 1, 1);
uvrr.push(0, 0, 1, 1, 0, 1);
}
let geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
// 设置几何体attributes属性的位置uv属性
geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array(uvrr), 2);
geometry.computeVertexNormals()
texture = new THREE.TextureLoader().load( "./img/warning.png" );
// texture = new THREE.TextureLoader().load( "./img/wall.png" );
texture.repeat.set( 5, 1 );
// 设置.wrapS也就是U方向,纹理映射模式(包裹模式)
texture.wrapS = THREE.RepeatWrapping;//对应offste.x偏移
// 设置.wrapT也就是V方向,纹理映射模式
texture.wrapT = THREE.RepeatWrapping;//对应offste.y偏移
let material = new THREE.MeshBasicMaterial({
map: texture,
color: "#FFFFFF",
side: THREE.DoubleSide,
transparent:true
});
let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh);
mesh.rotateX(-Math.PI / 2);
}
function threeStart() {
initThree(); //初始化Three.js渲染器等初始操作
initCamera(); //初始化相机
initScene(); //初始化场景
initLight(); //初始化灯光
initControls(); //初始化控制器
initObject(); //初始化渲染物体
// initAxesHelper();
render(); //执行渲染
}
function initAxesHelper() {
// AxesHelper:辅助观察的坐标系(红x、绿y、蓝z)
const axesHelper = new THREE.AxesHelper(1500);
scene.add(axesHelper);
}
function initControls() {
// 设置相机控件轨道控制器OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener("change", function () {
renderer.render(scene, camera); //执行渲染操作
}); //监听鼠标、键盘事件
}
function render() {
texture.offset.x -= 0.01;//设置纹理动画:偏移量根据纹理和动画需要,设置合适的值
renderer.render(scene, camera);
requestAnimationFrame(render);
}
</script>
</head>
<body onload="threeStart();">
<div id="webgl"></div>
</body>
</html>
视频地址:https://www.bilibili.com/video/BV1K7BRYeEyq/