ThreeJS-3D教学八-OBJLoader模型加载+动画

news/2024/5/17 17:36:34/文章来源:https://blog.csdn.net/weixin_44384273/article/details/133705042

先看效果图:
在这里插入图片描述
本篇给大家介绍下模型加载的知识,用到了OBJLoader对应的模型,为了增加趣味性,花了些时间,利用new THREE.Points获取到模型上的点,做了一个动画效果,其实就是操作 Y轴上的点,先降低上0,然后再还原,代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body {width: 100%;height: 100%;}* {margin: 0;padding: 0;}.label {font-size: 20px;color: #fff;font-weight: 700;}.btn {position: absolute;right: 0;top: 10px;}.button {width: 100px;height: 50px;}</style>
</head>
<body>
<div id="container"></div>
<div class="btn"><button class="btn3 button">开始动画</button><button class="btn4 button">暂停动画</button>
</div>
<script type="importmap">{"imports": {"three": "./three-155/build/three.module.js","three/addons/": "./three-155/examples/jsm/"}}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
let stats, labelRenderer, gpuPanel;
let points, speed = 0.5;
let clock = new THREE.Clock();
let flagStart = true;
let camera, scene, renderer, controls;
const group = new THREE.Group();
let widthImg = 200;
let heightImg = 200;
const dataObj = {verticesDown: 0,verticesUp: 0,direction: 0, // 运动的方向speed: 15, // 运动的速度start: Math.floor(100 + 200 * Math.random()),delay: Math.floor(200 + 200 * Math.random())
};
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加平面
// addPlane();// 创建模型加载器
let loader = new OBJLoader();
// loader.load('../materials/car.obj', function (loadedMesh) {
loader.load('./materials/chahu_xlo7pg.obj', function (loadedMesh) {let material = new THREE.PointsMaterial({color: 0xffffff,size: 1,// 使用 opacity 的前提是开启 transparentopacity: 0.8,transparent: true,// 设置元素与背景的融合模式blending: THREE.AdditiveBlending,// 指定粒子的纹理map: generateSprite(),// 用于去除纹理的黑色背景,关于 depthTest 和 depthWrite 的详细解释,// 请查看https://stackoverflow.com/questions/37647853/three-js_three-depthwrite-vs-depthtest-for-transparent-canvas-texture-map-on-three-pdepthTest: false});loadedMesh.children.forEach(function (child) {child.geometry.setAttribute( 'initialPosition', child.geometry.attributes.position.clone() );points = new THREE.Points(child.geometry, material);const scale = 15;points.scale.set(scale, scale, scale);points.position.y = 0;scene.add(points);});
});let btn3 = document.querySelector('.btn3');
let btn4 = document.querySelector('.btn4');btn3.addEventListener('click', () => {flagStart = true;
});
btn4.addEventListener('click', () => {flagStart = false;
});function clearIntervalFun() {if (upInterval) {clearInterval(upInterval);}if (downInterval) {clearInterval(downInterval);}
}function modelMove() {if (!points || !flagStart) return;let delta = 10 * clock.getDelta();delta = delta < 2 ? delta : 2;let positions = points.geometry.attributes.position;let initialPositions = points.geometry.attributes.initialPosition;let count = positions.count;if ( dataObj.start > 0 ) {dataObj.start -= 1;} else {if ( dataObj.direction === 0 ) {dataObj.direction = - 1;}}for ( let i = 0; i < count; i ++ ) {let px = positions.getX( i );let py = positions.getY( i );let pz = positions.getZ( i );// falling downif ( dataObj.direction < 0 ) {if ( py > 0 ) {positions.setXYZ(i,px + 1.5 * (0.50 - Math.random()) * speed * delta,py + 3.0 * (0.25 - Math.random()) * speed * delta,pz + 1.5 * (0.50 - Math.random()) * speed * delta);} else {dataObj.verticesDown += 1;}}// rising upif ( dataObj.direction > 0 ) {let px = initialPositions.getX( i );let py = initialPositions.getY( i );let pz = initialPositions.getZ( i );let pyNew = positions.getY( i );let pyT = pyNew + 0.03;if (pyT <= py) {positions.setXYZ(i,px + 1.5 * (0.50 - Math.random()) * speed * delta,pyT,pz + 1.5 * (0.50 - Math.random()) * speed * delta);} else {dataObj.verticesUp += 1;positions.setXYZ(i,px,py,pz);}}}// all vertices down 当每一个点 都运动到了底部 达到 countif ( dataObj.verticesDown >= count ) {if ( dataObj.delay <= 0 ) {dataObj.direction = 1; // 这时赋值大于0 开始向上运动dataObj.speed = 5;dataObj.verticesDown = 0;dataObj.delay = 320;} else {dataObj.delay -= 1;}}// all vertices upif ( dataObj.verticesUp >= count ) {if ( dataObj.delay <= 0 ) {dataObj.direction = - 1;dataObj.speed = 15;dataObj.verticesUp = 0;dataObj.delay = 120;} else {dataObj.delay -= 1;}}positions.needsUpdate = true;
}// 生成纹理
function generateSprite() {let canvas = document.createElement('canvas');canvas.width = 10;canvas.height = 10;let context = canvas.getContext('2d');// createRadialGradient(x0,y0,r0,x1,y1,r1),其中x0,y0,r0分别为起始圆的位置坐标和半径,x1,y1,r1为终止圆的坐标和半径。let gradient = context.createRadialGradient(canvas.width / 2,canvas.height / 2,0,canvas.width / 2,canvas.height / 2,canvas.width);gradient.addColorStop(0, 'rgba(255,255,255,1)');gradient.addColorStop(0.2, 'rgba(0,255,255,1)');gradient.addColorStop(0.4, 'rgba(0,0,64,1)');gradient.addColorStop(1, 'rgba(0,232,3,1)');context.fillStyle = gradient;// context.fillStyle = '#f00';context.fillRect(0, 0, canvas.width, canvas.height);let texture = new THREE.Texture(canvas);texture.needsUpdate = true;return texture;
}function addPlane() {// 创建一个平面 PlaneGeometry(width, height, widthSegments, heightSegments)const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);// 创建 Lambert 材质:会对场景中的光源作出反应,但表现为暗淡,而不光亮。const planeMaterial = new THREE.MeshPhongMaterial({color: 0xb2d3e6,side: THREE.DoubleSide});const plane = new THREE.Mesh(planeGeometry, planeMaterial);// 以自身中心为旋转轴,绕 x 轴顺时针旋转 45 度plane.rotation.x = -0.5 * Math.PI;plane.position.set(0, -4, 0);scene.add(plane);
}function init() {camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );camera.up.set(0, 1, 0);camera.position.set(100, 60, 100);camera.lookAt(0, 0, 0);scene = new THREE.Scene();scene.background = new THREE.Color( '#000' );renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setPixelRatio( window.devicePixelRatio );renderer.setSize( window.innerWidth, window.innerHeight );document.body.appendChild( renderer.domElement );labelRenderer = new CSS2DRenderer();labelRenderer.setSize( window.innerWidth, window.innerHeight );labelRenderer.domElement.style.position = 'absolute';labelRenderer.domElement.style.top = '0px';labelRenderer.domElement.style.pointerEvents = 'none';document.getElementById( 'container' ).appendChild( labelRenderer.domElement );controls = new OrbitControls( camera, renderer.domElement );controls.mouseButtons = {LEFT: THREE.MOUSE.PAN,MIDDLE: THREE.MOUSE.DOLLY,RIGHT: THREE.MOUSE.ROTATE};controls.enablePan = true;// 设置最大最小视距controls.minDistance = 20;controls.maxDistance = 1000;window.addEventListener( 'resize', onWindowResize );stats = new Stats();stats.setMode(1); // 0: fps, 1: msdocument.body.appendChild( stats.dom );gpuPanel = new GPUStatsPanel( renderer.getContext() );stats.addPanel( gpuPanel );stats.showPanel( 0 );scene.add( group );
}function initLight() {const light = new THREE.DirectionalLight(new THREE.Color('rgb(253,253,253)'));light.position.set(100, 100, -10);light.intensity = 3; // 光线强度light.castShadow = true; // 是否有阴影light.shadow.mapSize.width = 2048; // 阴影像素light.shadow.mapSize.height = 2048;// 阴影范围const d = 80;light.shadow.camera.left = -d;light.shadow.camera.right = d;light.shadow.camera.top = d;light.shadow.camera.bottom = -d;light.shadow.bias = -0.0005; // 解决条纹阴影的出现// 最大可视距和最小可视距light.shadow.camera.near = 0.01;light.shadow.camera.far = 2000;const AmbientLight = new THREE.AmbientLight(new THREE.Color('rgb(255, 255, 255)'), 0.1);// scene.add( light );scene.add( AmbientLight );
}function initHelp() {// const size = 100;// const divisions = 5;// const gridHelper = new THREE.GridHelper( size, divisions );// scene.add( gridHelper );// The X axis is red. The Y axis is green. The Z axis is blue.const axesHelper = new THREE.AxesHelper( 100 );scene.add( axesHelper );
}function axesHelperWord() {let xP = addWord('X轴');let yP = addWord('Y轴');let zP = addWord('Z轴');xP.position.set(50, 0, 0);yP.position.set(0, 50, 0);zP.position.set(0, 0, 50);
}function addWord(word) {let name = `<span>${word}</span>`;let moonDiv = document.createElement( 'div' );moonDiv.className = 'label';// moonDiv.textContent = 'Moon';// moonDiv.style.marginTop = '-1em';moonDiv.innerHTML = name;const label = new CSS2DObject( moonDiv );group.add( label );return label;
}function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize( window.innerWidth, window.innerHeight );
}function animate() {requestAnimationFrame( animate );if (points) {points.rotation.y += 0.005}modelMove();stats.update();controls.update();labelRenderer.render( scene, camera );renderer.render( scene, camera );
}
</script>
</body>
</html>

如果有同学从我第一篇文章开始学到现在,相信大家对 three 已经有了初步的认识,会不会感觉跟学习CSS3很相似的经历,其实就是多看案例,多试一些效果,多从案例中吸收知识点。
如果没有看的同学可以进入我的主页,依次查看。
编写不易,有帮到你的话,给个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_558450.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript 编写一个 数值转换函数 万以后简化 例如1000000 展示为 100万 万以下原来数值返回

很多时候 我们看一些系统 能够比较只能的展示过大的数值 例如 到万了 他就能展示出 多少 多少万 看着很奇妙 但实现确实非常的基础 我们只需要一个这样的函数 //数值转换函数 convertNumberToString(num) {//如果传入的数值 不是数字 且也无法转为数字 直接扔0回去if (!parse…

在asp.net中,实现类似安卓界面toast的方法(附更多弹窗样式)

目录 一、背景 二、操作方法 2.1修改前 2.2修改后 三、总结 附&#xff1a;参考文章&#xff1a; 一、背景 最近在以前的asp.net网页中&#xff0c;每次点击确定都弹窗&#xff0c;然后还要弹窗点击确认&#xff0c;太麻烦了&#xff0c;这次想升级一下&#xff0c;实现…

10分钟深入探讨带你彻底理解浅拷贝与深拷贝

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言 &#x1f4d8; 1. 深拷贝…

vue3+ts项目04-国际化

yarn add vue-i18n yarn add js-cookie yarn add types/js-cookiesrc下新建i18n文件夹&#xff0c;该文件夹下新建lang和pages文件夹&#xff0c; lang文件夹下新建en.ts // 定义内容 export default {router: {home: home,system: {system: system,menu: systemMenu,role: sy…

Pymol做B因子图

分子动力学模拟结束后&#xff0c;获得蛋白的平均结构&#xff0c; 比如获得的平均结构为WT-average.pdb 然后将平均结构导入到Pymol 中&#xff0c;可以得到B因子图。 gmx rmsf -f md_0_100_noPBC.xtc -s md_0_100.tpr -o rmsf-per-residue.xvg -ox average.pdb -oq bfactors…

Android App备案获取公钥、签名MD5值

1.生成签名文件 keytool -genkey -alias 别名XXX -keypass 密码XXX -keyalg RSA -keysize 2048 -validity 36500 -keystore D:\XXX.keystore -storepass 密码XXX2.查看签名MD5值 keytool -list -v -keystore D:\XXX.keystore3.查看公钥 导出证书XXX.cer keytool -export -a…

vue接入高德地图获取经纬度

&#x1f90d;step1:高德地图开放平台&#xff0c;根据指引注册成为高德开放平台开发者&#xff0c;并申请 web 平台&#xff08;JS API&#xff09;的 key 和安全密钥; &#x1f90d;step2:在html引入安全密钥&#xff08;获取经纬度用&#xff0c;不然会报错&#xff09; <…

uniapp上echarts地图钻取

1: 预期效果 通过切换地图 , 实现地图的钻取效果 2: 实现原理以及核心方法/参数 一开始是想利用更换地图数据的形式进行地图钻取 , 这就意味着我们需要准备全国30多个省份的地图数据 , 由于一开始考虑需要适配小程序端 , 如此多的地图文件增加了程序的体积 , 如果使用接口调…

spring 事务源码阅读之实现原理

开启事务 使用EnableTransactionManagement注解开启事务 该注解会引入TransactionManagementConfigurationSelector类&#xff0c;然后该类导入两个类AutoProxyRegistrar和ProxyTransactionManagementConfiguration。 1、添加bean后置处理器 AutoProxyRegistrar类的作用是注…

大数据学习(2)Hadoop-分布式资源计算hive(1)

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博>主哦&#x…

前端uniapp生成海报并保存相册

uiapp插件 目录 图片qrcode.vue源码完整版封装源码qrcodeSwiper.vue最后 图片 qrcode.vue源码完整版 <template><view class"qrcode"><div class"qrcode_swiper SourceHanSansSC-Normal"><!-- <cc-scroolCard :dataInfo"dat…

10-09 周一 图解机器学习之深度学习感知机学习

10-09 周一 图解机器学习之深度学习感知机学习 时间版本修改人描述2023年10月9日14:13:20V0.1宋全恒新建文档 简介 感知机是神经网络中的概念&#xff0c;1958年被Frank Rosenblatt第一次引入。感知机作为一种基本的神经网络模型&#xff0c;它模拟了人脑神经元的工作原理。感…

Python+requests+Excel数据驱动的接口自动化测试中解决接口间数据依赖

在实际的测试工作中&#xff0c;在做接口自动化过程中往往会遇到接口间数据依赖问题&#xff0c;即API_03的请求参数来源与API_02的响应数据&#xff0c;API_02的请求参数又来源与API_01的响应数据&#xff0c;因此通过自动化方式测试API_03接口时&#xff0c;需要预先请求API_…

山西电力市场日前价格预测【2023-10-11】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-11&#xff09;山西电力市场全天平均日前电价为507.37元/MWh。其中&#xff0c;最高日前电价为873.70元/MWh&#xff0c;预计出现在18: 45。最低日前电价为313.23元/MWh&#xff0c;预计…

阿里云数据库MongoDB恢复到本地

共两种方式&#xff0c;建议使用第二种的逻辑恢复&#xff0c;比较方便快捷 一、下载物理备份文件 下载的格式是xb的&#xff0c;主要跟实例创建时间有关&#xff0c;2019年03月26日之前创建的实例&#xff0c;物理备份文件格式为tar&#xff0c;后面全部都是xb的格式了&#…

PTA 7-3 插松枝(单调栈)

题目7-3 插松枝 人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上&#xff0c;做成大大小小的松枝。他们的工作流程&#xff08;并不&#xff09;是这样的&#xff1a; 每人手边有一只小盒子&#xff0c;初始状态为空。 每人面前有用不完的松枝干和一个推送器&#…

C++ 获取文件创建时间、修改时间、大小等属性

简介 获取文件创建时间、修改时间、大小等属性 代码 #include <iostream> #include <string.h> #include <time.h>void main() {std::string filename "E:\\LiHai123.txt";struct _stat stat_buffer;int result _stat(filename.c_str(), &s…

shein汇总

如何将mysql的数据同步到es 1、使用Canal&#xff1a;Canal是阿里巴巴开源的一款基于MySQL数据库增量日志解析和同步的工具。Canal可以将MySQL中的数据同步到ES中&#xff0c;支持多种数据格式和数据源。 2、使用自定义脚本&#xff1a;可以编写自定义脚本&#xff0c;通过My…

C语言利用计算机找系统的最小通路集的算法

背景&#xff1a; 有人求助到博主希望分析一下他们老师给出的题目&#xff0c;博主思路分析和解题过程如下 题目要求&#xff1a; 联络矩阵法&#xff0c;当 n 较小时可以用手算,当然也可以用计算机计算。但当 n 很大时&#xff0c;需要计 算机的容量很大才行。为此要探求有…

git 取消待推送内容

选择最后一次提交的记录&#xff0c;右键->软合并