threejs官方demo学习:模型加载

news2024/12/22 20:07:23

前言

案例太多了,考虑了一下,实际项目中有可能用的情况一般就是加载模型,然后对模型进行一些操作。因此打算好好看一下关于模型加载的案例,其他案例就不看了。

模型加载并改变材质

<script lang="ts" setup>
import { onMounted } from 'vue';
// threejs
import * as THREE from 'three';
// 引入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 模型加载器,用于加载3D Studio Max软件中的3DS和MAX文件格式
import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader';

onMounted(() => {
    console.log(document.getElementById('test'));
    // 创建一个场景
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xfff);
    // 创建一个相机
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 100);
    // 设置相机的位置
    camera.position.z = 5;
    // 将相机添加到场景中
    scene.add(camera);

    // 添加直线光,并设置光源位置
    const light1 = new THREE.DirectionalLight(0xffeedd);
    light1.position.set(0, 0, 2);
    scene.add(light1);
    const light2 = new THREE.DirectionalLight(0xffeedd);
    light2.position.set(0, 0, -2);
    scene.add(light2);

    // 创建一个渲染器
    const renderer = new THREE.WebGLRenderer({
        antialias: true // 设置抗锯齿
    });
    // 设置渲染尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 设置渲染的输出编码
    renderer.outputEncoding = THREE.sRGBEncoding;
    // 将内容渲染到页面中
    document.getElementById('test')?.appendChild(renderer.domElement);

    // 创建轨道控制器
    const control = new OrbitControls(camera, renderer.domElement);
    control.target.set(0, 0, 0);
    // 设置阻尼
    control.enableDamping = true;

    // 加载纹理
    const normal = new THREE.TextureLoader().load('../../../../public/normal.jpg');
    // 加载模型
    const loader = new TDSLoader();
    loader.load('../../../../public/portalgun.3ds', object => {
        // 遍历对象,给物体添加贴图
        object.traverse(child => {
            // 如果是物体,则修改物体的材质
            if (child.isMesh) {
                child.material = new THREE.MeshPhongMaterial({
                    color: 0xfff,
                    reflectivity: 0.7,
                    refractionRatio: 0.9,
                    envMap: normal
                });
            }
        });
        scene.add(object);
    });

    // 创建渲染函数
    const render = () => {
        control.update();
        renderer.render(scene, camera);
        // 通过动画帧来执行函数
        requestAnimationFrame(render);
    };

    render();
});

</script>

在这里插入图片描述

这里最重要的知识应该就是遍历模型,来修改模型的材质。

模型加载产生投影

<script setup lang="ts">
import { onMounted } from 'vue';
import * as THREE from 'three';

// 导入轨道控制器,模块化开发导入的是jsm不是js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { ThreeMFLoader } from 'three/examples/jsm//loaders/3MFLoader';

onMounted(() => {
    // 创建一个场景
    const scene = new THREE.Scene();
    // 设置背景
    scene.background = new THREE.Color(0x999999);
    // 创建相机
    const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 500);
    // 添加环境光
    scene.add(new THREE.AmbientLight(0x999999));
    // 设置相机位置和向上的方向
    camera.up.set(0, 0, 1);
    camera.position.set(100, 9, 50);
    scene.add(camera);
    // 添加平行光
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    // 设置光线的位置,设置光照产生阴影
    directionalLight.position.set(60, 10, 60);
    directionalLight.castShadow = true;
    // 设置了阴影贴图(二维的)的上下左右边界的坐标,x轴正方向代表右,y轴正方向代表上
    directionalLight.shadow.camera.top = 70;
    directionalLight.shadow.camera.bottom = -55;
    directionalLight.shadow.camera.left = -50;
    directionalLight.shadow.camera.right = 10;
    // 设置阴影的最近最远观察激励
    directionalLight.shadow.camera.near = 0.1;
    directionalLight.shadow.camera.far = 200;
    // 值越大越清晰
    directionalLight.shadow.mapSize.set(1024, 1024);
    scene.add(directionalLight);
    // 添加一个小球,来显示光照的位置
    const ball = new THREE.Mesh(
        new THREE.SphereGeometry(2, 32, 32),
        new THREE.MeshBasicMaterial({ color: 0xfff })
    );
    ball.position.set(60, 10, 60);
    scene.add(ball);
    // 添加坐标轴辅助器
    const axesHelper = new THREE.AxesHelper(100);
    scene.add(axesHelper);
    // 添加平面
    const ground = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000), new THREE.MeshPhongMaterial());
    ground.receiveShadow = true;
    scene.add(ground);
    // 加载模型
    const loader = new ThreeMFLoader();
    loader.load('../../../../public/truck.3mf', object => {
        // 设置模型的位置
        object.position.set(0, 0, -10);
        object.traverse(child => {
            child.castShadow = true;
        });
        scene.add(object);
    });

    // 创建渲染器、设置像素比、渲染尺寸
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 开启场景阴影渲染
    renderer.shadowMap.enabled = true;
    document.getElementById('container')?.appendChild(renderer.domElement);

    // 添加控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.target.set(0, 1.2, 2);
    controls.update();

    // 渲染函数
    const render = () => {
        renderer.render(scene, camera);
        requestAnimationFrame(render);
        controls.update();
    };
    render();
});
</script>

关于阴影加载的部分挺复杂的需要满足以下条件:

  • 添加的光源能够使物体产生投影
  • 物体本身能够产生投影
  • 地面能够接收物体产生的投影
  • 渲染器要开启场景的阴影渲染

满足了以上条件起始还不一定能够看见阴影,还与光源的位置有关,阴影的设置有关、相机的位置有关

  • 可以通过添加辅助坐标系(红色的x轴、绿色的y轴、蓝色的z轴)、添加一个小球来模拟光源的位置。通过不断的调整位置来产生一个合适的阴影
  • 要对阴影进行相关的设置,设置大小,清晰度
  • 可以添加一个小球来模拟相机,来调整相机的位置

执行模型本身的动画

<script setup lang="ts">
import { onMounted } from 'vue';
import * as THREE from 'three';
// 引入轨道
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 引入模型加载器
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';

onMounted(() => {
    const clock = new THREE.Clock();
    // 动画混合器对象
    let mixer;
    // 创建一个场景
    const scene = new THREE.Scene();
    // 设置背景
    scene.background = new THREE.Color(0xa0a0a0);
    // 添加雾气
    scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000);
    // 创建相机
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
    camera.position.set(100, 200, 300);
    scene.add(camera);
    // 添加渐变光
    const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
    hemiLight.position.set(0, 200, 0);
    scene.add(hemiLight);
    // 添加平行光、设置阴影
    const dirLight = new THREE.DirectionalLight(0xffffff);
    dirLight.position.set(0, 200, 100);
    dirLight.castShadow = true;
    dirLight.shadow.camera.top = 180;
    dirLight.shadow.camera.bottom = -100;
    dirLight.shadow.camera.left = -120;
    dirLight.shadow.camera.right = 120;
    scene.add(dirLight);

    //  添加平面是为了提供一个地面,让其他物体可以在其上运动和投射阴影。
    //  添加网格辅助对象是为了在场景中提供一个可视化的参考网格,方便调整其他物体的位置和大小。
    //  设置网格的透明度是为了让其不会完全遮挡其他物体,同时又能提供足够的参考信息。

    // 添加一个平面
    const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2000, 2000), new THREE.MeshPhongMaterial({ color: 0x999999, depthWrite: false }));
    mesh.rotation.x = -Math.PI / 2;
    mesh.receiveShadow = true;
    scene.add(mesh);
    // 添加一个网格辅助对象
    const grid = new THREE.GridHelper(2000, 20, 0x000, 0x000);
    // 开启透明,才能设置透明度
    grid.material.transparent = true;
    grid.material.opacity = 0.2;
    scene.add(grid);

    // 加载模型
    const loader = new FBXLoader();
    loader.load('../../../../public/SambaDancing.fbx', object => {
        console.log('对象', object);
        mixer = new THREE.AnimationMixer(object);
        // 创建一个动画剪辑action,使用mixer.clipAction方法,传入模型对象的第一个动画
        const action = mixer.clipAction(object.animations[0]);
        // 播放动画
        action.play();
        // 开启阴影效果
        object.traverse(child => {
            if (child.isMesh) {
                child.castShadow = true;
                child.receiveShadow = true;
            }
        });
        scene.add(object);
        animate();
    });

    // 创建渲染器,设置抗锯齿
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    // 设置像素比
    renderer.setPixelRatio(window.devicePixelRatio);
    // 渲染尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 开启阴影渲染
    renderer.shadowMap.enabled = true;
    // 将内容添加到dom中
    document.getElementById('container')?.appendChild(renderer.domElement);

    // 添加轨道
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置轨道的目标位置,用于控制相机绕着场景的中心点旋转的方法
    controls.target.set(0, 100, 0);
    controls.update();

    // 监听容器的变化
    window.addEventListener('resize', () => {
        // 更新相机的视锥体
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        // 更新渲染大小
        renderer.setSize(window.innerWidth, window.innerHeight);
    });

    // 渲染方法
    const animate = () => {
        requestAnimationFrame(animate);
        // 获取时间差(delta)以便于在动画播放时进行帧率控制
        const delta = clock.getDelta();
        if (mixer) {
            mixer.update(delta);
        }
        renderer.render(scene, camera);
    };
});

这里有个点要注意:动画是根据时间差来进行播放的,因此const clock = new THREE.Clock();不要放在动画执行函数里
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 FPGA 的单脉冲技术:算法设计(附源码)

一、前言 本例显示了开发单脉冲技术的工作流程的前半部分&#xff0c;其中信号使用数字下变频&#xff08;DDC&#xff09;进行下变频。本例中的模型适合在FPGA上实现。本示例重点介绍单脉冲技术的设计&#xff0c;以估计物体的方位角和仰角。 示例的第二部分是基于FPGA的单脉冲…

【C++】hash:unordered_map和unordered_set的底层结构

hash 哈希概念哈希冲突哈希函数哈希冲突的两种解决方法之闭散列哈希冲突的两种解决方法之开散列开散列和闭散列的比较 哈希概念 在c98中还并没有提出哈希这样的结构&#xff0c;只有以红黑树为底层结构的map&#xff0c;set系列&#xff0c;这样使得查询时的效率 l o g 2 N lo…

c语言修炼第二章--分支与循环

目录 前言 2.1分支语句 2.1.1什么是语句 2.2分支语句 2.2.1if语句的语法形式 2.2.2悬空else问题 2.2.3if书写形式的对比 2.3switch语句 2.3.1switch的语法形式 2.3.2 switch与break的关系 2.3.3 switch与default的关系 前言 小伙伴们端午安康吖&#xff01;今天我们…

Mac菜单栏快捷开关 One Switch

One Switch是一款简单易用的 macOS 应用程序&#xff0c;它可以帮助用户快速地切换和管理 Mac 电脑的各种系统设置。这款应用程序提供了一个干净简洁的界面&#xff0c;让用户可以快速地启用或禁用诸如 Wi-Fi、蓝牙、Night Shift、暗色模式、Do Not Disturb 等功能。用户可以通…

建立无需build的vue单页面应用SPA框架

vue、react这种前端渲染的框架&#xff0c;比较适合做SPA&#xff08;Single Page Application&#xff09;。如果用ejs做SPA&#xff0c;js代码控制好全局变量冲突不算严重&#xff0c;但dom元素用jquery操作会遇到很多的名称上的冲突&#xff08;tag、id、name&#xff09;。…

运动蓝牙耳机怎么选、好用的运动蓝牙耳机推荐

在快节奏的现代生活中&#xff0c;运动成为了释放压力、保持健康和放松身心的重要方式。跑步、健身、骑行等各类运动成为了人们日常生活中不可或缺的一部分。然而&#xff0c;一场精彩的运动体验离不开动感的音乐伴奏。为了满足人们对高品质音乐的追求&#xff0c;一款出色的运…

nvm安装、管理多个node版本

1、官网下载nvm https://github.com/coreybutler/nvm-windows/releases 2、解压ZIP&#xff0c;双击nvm-setup.exe(假如以前安装了node.js&#xff0c;把以前的卸载了再安装nvm) 3、安装nvm 注意&#xff1a;这一步的nodejs目录需要手动创建 4、安装完毕&#xff0c;设置下载镜…

OpenCV动态人物识别代码

动态人物识别代码 int main() {// 打开视频文件或摄像头VideoCapture cap("vtest.avi");// VideoCapture cap(0); // 使用默认摄像头if (!cap.isOpened()){std::cout << "无法打开视频文件或摄像头流" << std::endl;return -1;}// 读取第一帧…

幽灵依赖是什么,pnpm出现的意义,使用pnpm创建一个vue3项目

什么是幽灵依赖&#xff08;幻影依赖&#xff09; 比如我们创建一个全新的vue3项目 然后我们正常地通过npm install来下载依赖 然后我们发现&#xff0c;node_ modules文件夹下的很多依赖&#xff0c;我们在package.json中明明没去声明却都下载下来了 那么这些没声明却下载…

SQL-游标-查询

/***DB版本&#xff1a;SQL Server 2022***/ --切换数据库 use MyDatabase--创建游标(scroll&#xff1a;滚动游标) declare mycur cursor scroll for select EmpNo from Employee--打开游标 open mycur --提取第一行 fetch first from mycur --提取最后一行 fetch last from m…

ansible自动化安装及简单操作

目录 一、运行机制 二、安装 1.下载ansible 2.配置免密 3.修改配置文件 4.创建主机清单 5.远程安装 6.远程卸载 一、运行机制 Ansible&#xff1a; ansible的核心模块 Host Inventory&#xff1a;主机清单&#xff0c;也就是被管理的主机列表 Playbooks&…

实战打靶集锦-021-glasgowsmile

提示&#xff1a;本文记录了博主的一次曲折的打靶经历。 目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 手工访问4.2 目录枚举4.3 手工探查4.4 搜索EXP4.5 joomlascan4.6 用户猜测与密码爆破4.7 构建反弹shell 5. 提权5.1 优化shell5.2 枚举系统信息5.3 探查/etc/pass…

虚拟机VMware+Ubuntu配置DPDK环境并运行Helloworld

虚拟机VMwareUbuntu配置DPDK环境并运行Helloworld 文章目录 虚拟机VMwareUbuntu配置DPDK环境并运行Helloworld安装虚拟机虚拟机中安装DPDK运行Helloworld 首先需要强调的是&#xff0c;版本的影响很大&#xff0c;有可能会因为版本不匹配而导致无法成功配置DPDK环境。 安装虚拟…

【Leetcode -637.二叉树的层平均值 -671.二叉树中第二小的节点】

Leetcode Leetcode -637.二叉树的层平均值Leetcode -671.二叉树中第二小的节点 Leetcode -637.二叉树的层平均值 题目&#xff1a;给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10^(- 5) 以内的答案可以被接受。 示例 1&#xff1…

TCP服务器的C#实现

1、TCP实现类 internal class TcpServer{public Socket ServerSocket { get; set; }public Dictionary<string,Socket> Sockets { get; set; } new Dictionary<string,Socket>();public byte[] SendBuffer { get; set; }public byte[] ReceiveBuffer { get; set; …

Qt消息对话框

一、头文件及类型 #include<QMessageBox> 二、错误、信息、提示、警告演示 错误对话框 QMessageBox::critical(this,"critical","错误"); 信息对话框 QMessageBox::information(this,"info","信息"); 提问对话框 if(QMessageBo…

发布自己的第一个抖音小程序

结构与微信小程序一样 内嵌H5网页&#xff0c;适用于任何平台&#xff0c;同样也是使用web-view组件 <web-view src"https://some-domain/some/path"></web-view> 相比起来&#xff0c;它比微信小程序更加简化&#xff0c;开发会更方便了。 API查询地…

超越Java 7,迎接Java 8时代!掌握这些新特性提升你的编程技能!

大家好&#xff0c;我是小米&#xff0c;一个热衷于技术分享的小伙伴。今天&#xff0c;我将向大家介绍Java 8中的一些新特性。Java 8作为一次重大更新&#xff0c;引入了许多令人激动的新功能&#xff0c;让我们的编码变得更加简洁高效。接下来&#xff0c;我们将详细介绍这些…

【暂时解决】radio单选框的change事件执行两次

项目场景&#xff1a; 简单的单选框场景 选择国内地区&#xff0c;省市县的下拉框就显示&#xff0c;选择国外地区就隐藏。 问题描述 当我使用radio的change事件时&#xff0c;会执行两次 javascript&#xff1a; $(input[typeradio][nameoptionsRadios]).change(function …

黑产科普丨揭秘游戏黑灰产业链

自今年起&#xff0c;游戏版号已恢复常态化发放&#xff0c;游戏行业在官方发文肯定、重获资本青睐、AI降本增效等多方助力下持续回暖。暑期档将至&#xff0c;游戏厂商为了抢占更多的市场份额&#xff0c;占据更多的玩家视野&#xff0c;将有大量的游戏选择在这个时间上线。 …