用three.js做一个3D汉诺塔游戏(下)

为桌台添加材质纹理

为物体添加适当的材质纹理,可以使其视觉效果产生质的飞跃。接下来,我们将为桌台添加一种木质纹理,用到的纹理贴图来自Pixabay.com。

我们使用 TextureLoader 来加载纹理贴图,其 load 方法第1个参数为贴图的 URL 字符串,该方法返回一个纹理对象,可直接赋值给材质对象的颜色贴图属性 map。代码实现如下:

class Table {constructor({ width, height, depth }) {const geometry = new THREE.BoxGeometry(width, height, depth);// 纹理贴图const url = 'https://cdn.pixabay.com/photo/2016/12/26/13/47/fresno-1932211_1280.jpg';const material = new THREE.MeshLambertMaterial({ color: '#cccca6',map: new THREE.TextureLoader().load(url)  // 纹理贴图});return new THREE.Mesh(geometry, material);}
}

然而,我们发现这样做并不完美:由于纹理贴图存在网络加载延时,所以在贴图加载完成前,桌台始终是黑色的,只有贴图加载完成后,桌台才一瞬间有了外观。如下图所示:

对此,我们需要在技术上做出一些改进,来解决纹理贴图加载前后变化的突兀感。这里有2种改进方案:

  1. 预加载,等纹理贴图加载完成后,再生成带纹理效果的桌台;
  2. 渐进式加载,桌台先显示默认颜色,等纹理贴图加载完成后,再附加纹理效果。

这里我们选择方案2,因为方案2不会阻塞桌台的渲染,有着更好的用户体验。渐进式加载的原理就是在贴图加载完成后,标记材质对象的 needsUpdate 属性为 true,这样渲染器会在下一个渲染循环动态更新材质的纹理。核心代码如下:

const material = new THREE.MeshLambertMaterial({ color: '#cccca6' });// 动态更新材质纹理
new THREE.TextureLoader().load(url, (texture) => {material.needsUpdate = true;material.map = texture;
});

加载效果如下图所示:

优化光照效果

在 three.js 中,反光材质的物体表面会因为光照的不同而呈现出不同的明暗效果,其中光源的强弱、照射面和光线夹角等参数都会对物体的渲染效果产生影响。目前我们的场景效果并不理想:柱杆看上去灰蒙蒙的,盘子则是透出一股廉价的塑料味,都缺乏真实感。正所谓,效果不够,光照来凑,我们来调整光源参数,优化光照效果,让场景更加自然、真实。

让我们先对 Lights 类进行改造,新增一个距离参数,作为调整光源位置的基准值。我们将桌台长度、柱杆高度和桌台宽度分别作为光源在 x、y、z 方向上的位置基准值进行传递,以便于更加精确地设置光源位置,达到更好的照明效果。

class Lights {constructor({ directionX, directionY, directionZ }) {...}
}const presenter = {init() {...const lights = new Lights({directionX: model.tableSize.width,directionY: model.pillarSize.height,directionZ: model.tableSize.depth});}...
};

接下来,我们需要对之前已有的平行光源位置进行调整。为了更直观地调试光照效果,我们可以添加 DirectionalLightHelper 来帮助我们更好地观察光源位置平面和光照方向。

class Lights {constructor({ directionX, directionY, directionZ }) {const ambientLight = new THREE.AmbientLight('#fff', 1);  // 环境光const directLight = new THREE.DirectionalLight('#fff', 3);  // 平行光directLight.position.set(-directionX / 3, directionY * 4, directionZ * 1.5);const directLightHelper = new THREE.DirectionalLightHelper(directLight, 1, '#f00');return [ambientLight, directLight, directLightHelper];}
}

经过这一步平行光源的位置调整,我们看到柱杆和盘子已经变得光滑透亮。(下图中的红线为平行光源辅助观察线)

最后我们再添加一个米黄色的聚光灯光源,中和下场景的“高冷”基调。

class Lights {constructor(...) {...const spotLight = new THREE.SpotLight('#fdf4d5');spotLight.position.set(5, directionY * 4, 0);spotLight.angle = Math.PI / 2;  // 光线照射范围角度spotLight.power = 2000;  // 光源功率(流明)const spotLightHelper = new THREE.SpotLightHelper(spotLight, '#00f');return [ambientLight, directLight, directLightHelper, spotLight, spotLightHelper];}
}

完成后的效果如下图所示:(下图中的蓝线为聚光灯光源辅助观察线)

开启阴影效果

伴随着光源一起的自然是阴影,开启阴影能显著增强物体的立体效果。在现实世界中,阴影的产生需要光源、被照射物体和显示阴影的地方,这三者缺一不可。

在 three.js 中,出于性能考虑,实时渲染的阴影默认是关闭的,如果想要实现阴影效果,需要进行一番设置。与现实世界阴影的生成相似,这些设置都与光源、被照射物和阴影显示物有关,下面我们来逐一进行设置。

  1. 渲染器 开启阴影渲染支持

    const rendererView = {init(...) {...this.renderer.shadowMap.enabled = true;this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;},...
    };
    

    THREE.PCFSoftShadowMap 是 Three.js 中的一种阴影映射技术,它使用了 Percentage Closer Filtering (PCF) 和 Soft Shadows 技术来实现更加真实的阴影效果。PCF 技术可以减少阴影的锯齿感,Soft Shadows 技术可以让阴影边缘更加柔和。

  2. 光源 开启阴影投射,使被照射物体产生阴影

    directLight.castShadow = true;  // 为平行光源开启阴影投射
    
  3. 调整光源的 阴影相机 参数,控制阴影的渲染范围到合适大小

    超出阴影相机范围的阴影不会被渲染,所以要将阴影相机的范围扩大到能完整包含柱杆和盘子。使用 CameraHelper 辅助对象可以帮助我们更好的观测阴影相机的视野范围。

    directLight.shadow.camera.left = -directionX;
    directLight.shadow.camera.right = directionX;
    directLight.shadow.camera.top = directionZ;
    directLight.shadow.camera.bottom = -directionZ;const shadowCamera = new THREE.CameraHelper(directLight.shadow.camera);
    
  4. 允许 被照射物体 产生阴影

    这里设置允许柱杆和盘子产生阴影,并且允许其他物体产生的阴影可以投射到它们表面(接收阴影)。需要注意,castShadow 和 receiveShadow 要设置到 Mesh 对象上,不能设置到 Group 上。

    /* 柱杆 */
    class Pillar {constructor(...) {...const body = new THREE.Mesh(geometry, material);body.castShadow = true;  // 允许产生阴影body.receiveShadow = true;  // 允许接收阴影...},...
    }/* 盘子阴影设置同上 */
    clas

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

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

相关文章

Linux服务器上搭建深度学习环境(安装anaconda、创建虚拟环境、安装pytorch)

Linux服务器的搭配 Linux服务器上安装anaconda创建虚拟环境linux上安装pytorchxshell连接服务器 Linux服务器上安装anaconda 链接 创建虚拟环境 参考教程:此处 linux上安装pytorch 链接 xshell连接服务器 链接

科技动态人工智能应用太空探索生物科技

根据最新的科技资讯,以下是一些值得关注的科技动态: 人工智能领域 智能体热潮 :随着大模型的研发热潮,AI智能体的发展迅速,它们被用作认知核心,具备强大的学习和迁移能力。智能体的架构和交互方式也在不断进…

HTML — 弹性布局(1)

介绍 对于弹性布局,首先了解其概念。弹性布局,即flex box,是一种用于在容器中进行布局的CSS技术。它使得容器内的子元素能够以灵活的方式排列、对齐、分配空间,以便应对各种屏幕尺寸和设备类型。任何一个容器都可以指定为flex布局…

python课后习题三

题目&#xff1a; 解题过程&#xff1a; 模式A&#xff1a; num int(input("&#xff08;模式A&#xff09;输入数字&#xff1a;")) for i in range(num): for j in range(num): if j < i 1: …

MVP模式

1、创建数据库连接类&#xff1a; package com.db.mvp; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; //数据库连接类 public class DatabaseManager {private static DatabaseManager instance;private Connection connection…

flutter如何实现一个应用位于前台时全局页面每隔三分钟弹出一次一天最多弹出5次的GroMore半插屏广告,处于付费页和后台时停止

1&#xff0c;首先添加一个全局的生命周期监听类 class AppLifecycleObserver with WidgetsBindingObserver {bool IsCold false;bool isAgree false;void getIsCold() async {isAgree await SPManager().getBool(SPKeys.isAgreePrivacy, defaultValue: true);IsCold awai…

MQ的延迟队列

1&#xff0c;场景 1.定时发布文章 2.秒杀之后&#xff0c;给30分钟时间进行支付&#xff0c;如果30分钟后&#xff0c;没有支付&#xff0c;订单取消。 3.预约餐厅&#xff0c;提前半个小时发短信通知用户。 A -> 13:00 17:00 16:30 延迟时间&#xff1a; 7*30 * 60 * …

Linux_Debian学习笔记

文章目录 系统管理软件源Debian11debian11 ustc中国科技大学软件源debian11 清华大学软件源debian11 阿里云软件源 Debian12debian12 清华大学软件源 系统全局设置debian12 修改静态IP地址修改语言环境成中文 系统输入法Debian11 fcxe 安装rime中州韵五笔输入法 常用软件安装Do…

Python操作SQLite数据库

SQLite 是一种轻量级的数据库引擎&#xff0c;广泛用于嵌入式设备和小型应用程序。在Python中&#xff0c;SQLite 是一个流行的选择&#xff0c;因为它易于使用、快速、可靠&#xff0c;并且无需独立的服务器进程。本文将深入探讨如何使用 Python 操作 SQLite 数据库&#xff0…

Excel·VBA考勤打卡记录整理

看到一个帖子《excel吧-考勤一天四次打卡&#xff0c;快速找出缺卡》&#xff0c;每个人每天有4次打卡记录&#xff0c;需要整理出所有缺少的打卡记录 与之前的文章《ExcelVBA考勤打卡记录统计结果》结果形式类似 与之前的文章《ExcelVBA考勤打卡记录数据整理》查找上下班打卡…

Linux、Docker、Brew、Nginx常用命令

Linux、Docker、Brew、Nginx常用命令 Linuxvi编辑器文件操作文件夹操作磁盘操作 DockerBrewNginx参考 Linux vi编辑器 Vi有三种模式。命令模式、输入模式、尾行模式&#xff0c;简单的关系如下&#xff1a; i -- 切换到输入模式&#xff0c;在光标当前位置开始输入文本。&a…

【go从入门到精通】初识struct结构体

作者简介&#xff1a; 高科&#xff0c;先后在 IBM PlatformComputing从事网格计算&#xff0c;淘米网&#xff0c;网易从事游戏服务器开发&#xff0c;拥有丰富的C&#xff0c;go等语言开发经验&#xff0c;mysql&#xff0c;mongo&#xff0c;redis等数据库&#xff0c;设计模…

Flutter中工厂方法的多种实现方法与使用场景分析

在Flutter应用程序的开发中&#xff0c;使用工厂方法是一种常见的设计模式&#xff0c;它可以帮助我们更好地组织和管理代码&#xff0c;提高代码的可读性和可维护性。本文将介绍Flutter中工厂方法的多种实现方法&#xff0c;并分析其在不同场景下的使用情况。 什么是工厂方法…

虚拟网络设备性能优化

在现代网络架构中&#xff0c;虚拟网络设备扮演着越来越重要的角色&#x1f310;&#xff0c;特别是在云计算☁️和容器化技术&#x1f4e6;广泛应用的背景下。虚拟网络设备如虚拟以太网设备&#xff08;veth&#xff09;、虚拟交换机&#xff08;vSwitch&#xff09;、和虚拟路…

【阅读笔记】《同意》

未成年幼女与男恋童癖的故事 作者: [法]瓦内莎斯普林格拉 翻译&#xff1a;李溪月 kindle看的电子书 笔记 传记形式的书。作者记录了14岁时与一个50岁的恋童癖患者进行第一次性行为的经历以及前后的故事。 恋童癖被作者命名为“G”&#xff0c;作者自称“M”&#xff0c;G是…

适用于 Mac 的 10 大数据恢复工具,具有优点、缺点

数据丢失很常见&#xff0c;并且可能由于许多不同的原因而发生。这种情况在我和我们团队的其他成员身上发生过很多次&#xff0c;即使我们格外小心我们的个人存储设备。 幸运的是&#xff0c;数据恢复软件在大多数情况下都可以工作。但是&#xff0c;由于数据丢失场景彼此之间…

arcgis10.x创建镶嵌数据集

在ArcGIS中创建新的镶嵌数据集的步骤如下&#xff1a;打开ArcGIS软件&#xff0c;并在工具箱中选择“Data Management Tools.tbx”→“Raster”→“Mosaic Dataset”→“Create Mosaic Dataset”。在弹出的窗口中&#xff0c;配置镶嵌数据集的地理数据库位置、名称和坐标系…

centos7 安装 rabbitmq3.8.5

1.首先安装 erlang 环境&#xff1a; curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash sudo yum install erlang-21.3.8.14-1.el7.x86_64 yum install socat -y 2.安装 rabbitmq https://github.com/rabbitmq/rabbitmq-s…

STM32学习和实践笔记(7):改变系统时钟的实验

今天完成了这个改变系统时钟的实验。实验是成功的。 #include "stm32f10x.h" #include "led.h"void delay(u32 i) {while(i--); }void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间&#xff08;可以修改时钟&#xff09; {RCC_DeInit(); //将外设R…

【CKA模拟题】边车容器Shared-Volume的具体用法

Useful Resources: Persistent Volumes Claim , Pod to Use a PV 题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetes An existing nginx pod, my-pod-cka and Persistent Volume Claim…