【threejs教程7】threejs聚光灯、摄影机灯和汽车运动效果

【图片完整效果代码位于文章末】

        在上一篇文章中我们实现了汽车模型的加载,这篇文章主要讲如何让汽车看起来像在运动。同时列出聚光灯和摄像机灯光的加载方法。

        查看上一篇👉【threejs教程6】threejs加载glb模型文件(小米su7)👈

         往期文章:

         threejs基础开发应用示例

        【threejs教程2】threejs物体点击交互事件

        【threejs教程3】threejs物体轮廓发光

        【threejs教程4】threejs添加跳动标注

        【threejs教程5】threejs添加文字标注,且始终面向屏幕

实现原理

        汽车实际还是在原地,只有底部的地面纹理在不断地偏移,使汽车看起来像在运动。

实现步骤

1. 添加地板

        纹理图如下,水印好像去不了,需要无水印纹理的话可以跟我要。也可以自己截取一下把水印裁掉,或者自己找个别的纹理也行。

// 加载地面
function loadGround() {
  // 加载纹理
  const textureLoader = new THREE.TextureLoader();
  const floorTexture = textureLoader.load('./img/shuini.jpg'); // 替换为你的jpg文件路径
  // 设置纹理重复以覆盖整个地板
  floorTexture.wrapS = THREE.RepeatWrapping;
  floorTexture.wrapT = THREE.RepeatWrapping;
  floorTexture.repeat.set(1, 10);
  // 创建地板的材质
  const floorMaterial = new THREE.MeshStandardMaterial({ map: floorTexture });
  // 创建地板的几何体
  const floorGeometry = new THREE.PlaneGeometry(10, 60); // 参数为宽度和长度
  // 结合几何体和材质创建网格
  const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
  // 将地板沿Y轴旋转-90度使其与相机视角垂直
  floorMesh.rotation.x = -Math.PI / 2;
  // 添加地板到场景
  scene.add(floorMesh);
}

2.让地板的纹理运动

        不断更新地板在y轴方向的偏移量,即可达到不断重复运动的效果。

  function animate() {
    requestAnimationFrame(animate);
    // 更新纹理偏移量,这里只在V轴(纵向)上移动
    floorTexture.offset.y += 0.004; // 每帧偏移0.004,根据需要调整速度
    if (floorTexture.offset.y > 1) {
      floorTexture.offset.y -= 1; // 当偏移到下一个重复时重置
    }
  }
  animate(); // 开始动画循环

3.添加光源

我们添加了一个聚光灯和一个摄像机灯光效果,可以根据需求自己调整参数。如果觉得显示不佳也可以添加辅助灯光PointLight点光源和DirectionalLight平行光源等。

function addspotLight() {
  // 创建聚光灯
  const spotLight = new THREE.SpotLight(0xffffff, 1); // 光的颜色和强度
  spotLight.position.set(0, 5, 0); // 调整光源位置,这里假设汽车位于原点附近
  spotLight.castShadow = true; // 开启阴影投射
  spotLight.angle = Math.PI / 4; // 灯光锥角,控制光照的圆形范围大小
  spotLight.penumbra = 0.1; // 半影软边宽度,增加真实感
  spotLight.decay = 1; // 光照随着距离增加的衰减系数,影响光照范围
  // 设置目标为汽车的位置,假设carMesh是您的汽车模型
  spotLight.target = carMesh;
  // 将聚光灯添加到场景中
  scene.add(spotLight);
  scene.add(spotLight.target);
}
function addCameraLight() {
  // 创建光源
  const cameraLight = new THREE.PointLight(0xffffff, 0.8); // 白色点光,强度1
  cameraLight.castShadow = true; // 允许投射阴影(如果需要)
  scene.add(cameraLight);
  function updateCameraLight() {
    // 更新光源的位置
    cameraLight.position.copy(camera.position);
    // 更新光源的方向(对于DirectionalLight,确保它指向相机的前方)
    cameraLight.target.position.copy(camera.position);
    cameraLight.target.position.add(
      camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(-1)
    );
    cameraLight.lookAt(cameraLight.target.position);
  }
  function animate() {
    requestAnimationFrame(animate);
    // 保持光源与相机同步
    updateCameraLight();
    renderer.render(scene, camera);
  }
  animate();
}

4.完整效果代码如下所示

<template></template>
<script setup>
import * as THREE from 'three';
import { onMounted, ref } from 'vue';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';
const scene = new THREE.Scene();
let carMesh;
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
const controls = new OrbitControls(camera, renderer.domElement);

onMounted(() => {
  init();
});
function init() {
  camera.position.set(-5, 3, -3);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
  controls.update();
  function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
  }
  animate();
  // addLight();
  loadGround();
}
// 添加汽车模型
const loader = new GLTFLoader();
const dracoloader = new DRACOLoader();
dracoloader.setDecoderPath('./draco/gltf/');
loader.setDRACOLoader(dracoloader);
loader.load('./model/xiaomisu7.glb', (gltf) => {
  carMesh = gltf.scene;
  scene.add(carMesh);
  carMesh.position.y = 0.1;
  console.log(carMesh);
  carMesh.traverse((child) => {
    if (child.isMesh && child.name.includes('车轮')) {
      function animate() {
        requestAnimationFrame(animate);
        child.rotation.x -= 0.05;
      }
      animate();
    }
  });
  addspotLight();
  addCameraLight()
});
// 加载地面
function loadGround() {
  // 加载纹理
  const textureLoader = new THREE.TextureLoader();
  const floorTexture = textureLoader.load('./img/shuini.jpg'); // 替换为你的jpg文件路径
  // 设置纹理重复以覆盖整个地板
  floorTexture.wrapS = THREE.RepeatWrapping;
  floorTexture.wrapT = THREE.RepeatWrapping;
  floorTexture.repeat.set(1, 10);
  // 创建地板的材质
  const floorMaterial = new THREE.MeshStandardMaterial({ map: floorTexture });
  // 创建地板的几何体
  const floorGeometry = new THREE.PlaneGeometry(10, 60); // 参数为宽度和长度
  // 结合几何体和材质创建网格
  const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
  // 将地板沿Y轴旋转-90度使其与相机视角垂直
  floorMesh.rotation.x = -Math.PI / 2;
  // 添加地板到场景
  scene.add(floorMesh);
  function animate() {
    requestAnimationFrame(animate);
    // 更新纹理偏移量,这里只在U轴(横向)上移动
    floorTexture.offset.y += 0.004; // 每帧偏移0.01,根据需要调整速度
    if (floorTexture.offset.y > 1) {
      floorTexture.offset.y -= 1; // 当偏移到下一个重复时重置
    }
  }
  animate(); // 开始动画循环
}
function addspotLight() {
  // 创建聚光灯
  const spotLight = new THREE.SpotLight(0xffffff, 1); // 光的颜色和强度
  spotLight.position.set(0, 5, 0); // 调整光源位置,这里假设汽车位于原点附近
  spotLight.castShadow = true; // 开启阴影投射
  spotLight.angle = Math.PI / 4; // 灯光锥角,控制光照的圆形范围大小
  spotLight.penumbra = 0.1; // 半影软边宽度,增加真实感
  spotLight.decay = 1; // 光照随着距离增加的衰减系数,影响光照范围
  // 设置目标为汽车的位置,假设carMesh是您的汽车模型
  spotLight.target = carMesh;
  // 将聚光灯添加到场景中
  scene.add(spotLight);
  scene.add(spotLight.target);
}

function addCameraLight() {
  // 创建光源
  const cameraLight = new THREE.PointLight(0xffffff, 0.8); // 白色定向光,强度1
  cameraLight.castShadow = true; // 允许投射阴影(如果需要)
  scene.add(cameraLight);
  function updateCameraLight() {
    // 更新光源的位置
    cameraLight.position.copy(camera.position);
    // 更新光源的方向(对于DirectionalLight,确保它指向相机的前方)
    cameraLight.target.position.copy(camera.position);
    cameraLight.target.position.add(
      camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(-1)
    );
    cameraLight.lookAt(cameraLight.target.position);
  }
  function animate() {
    requestAnimationFrame(animate);
    // 保持光源与相机同步
    updateCameraLight();
    renderer.render(scene, camera);
  }
  animate();
}
</script>

 文章如有技术相关错误请各位批评指正

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

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

相关文章

详解23种设计模式——单例模式

单例模式 | CoderMast编程桅杆单例模式 单例模式是最常用的设计模式之一&#xff0c;他可以保证在整个应用中&#xff0c;某个类只存在一个实例化对象&#xff0c;即全局使用到该类的只有一个对象&#xff0c;这种模式在需要限制某些类的实例数量时非常有用&#xff0c;通常全局…

The Clock and the Pizza [NeurIPS 2023 oral]

本篇文章发表于NeurIPS 2023 (oral)&#xff0c;作者来自于MIT。 文章链接&#xff1a;https://arxiv.org/abs/2306.17844 一、概述 目前&#xff0c;多模态大语言模型的出现为人工智能带来新一轮发展&#xff0c;相关理论也逐渐从纸面走向现实&#xff0c;影响着人们日常生活…

WebAssembly学习记录

1.WebAssembly 1.1 指令集 概念&#xff1a;二进制编码集合。 依据计算机组成原理和计算机概论&#xff0c;指令集是一组二进制编码。 作用&#xff1a;控制硬件。 这些二进制指令直接作用于硬件电路&#xff0c;控制硬件完成指定操作。 例如&#xff1a;控制数据进入某个寄存…

如何通过质构分析仪客观评价面包的硬度、咀嚼性等口感指标

如何通过质构分析仪客观评价面包的硬度、咀嚼性等口感指标 一、引言&#xff1a;面包口感品质的重要性 面包作为日常生活中常见的食品之一&#xff0c;其口感品质直接影响到消费者的购买决策和食用体验。其中&#xff0c;硬度和咀嚼性是衡量面包口感品质的重要指标。因此&…

车企如何利用数据技术,指导汽车全生命周期的业务运营?

引言&#xff1a;数据正作为重点&#xff0c;为行业提供不可或缺的指导 《汽车数据发展研究报告&#xff08;2023&#xff09;》指出&#xff0c;汽车行业正由传统硬件制造向“电动化、智能化、网联化”方向转变。德勤预测&#xff0c;到 2025 年&#xff0c;汽车行业 20%的利…

小程序AI智能名片S2B2C商城系统:四大主流商业模式深度解析与实战案例分享

在私域电商迅速崛起的大背景下&#xff0c;小程序AI智能名片S2B2C商城系统以其独特的商业模式和强大的功能&#xff0c;正成为品牌商们争相探索的新领域。在这一系统中&#xff0c;拼团模式、会员电商、社区团购和KOC营销等四种主流模式&#xff0c;为品牌商提供了多样化的营销…

深度探讨容器化技术在网络安全中的应用与挑战

随着容器化技术的快速发展&#xff0c;尤其是Docker与Kubernetes&#xff08;K8s&#xff09;的广泛应用&#xff0c;企业IT架构正经历着从传统虚拟机向轻量级容器的深刻变革。容器化技术为提升资源利用率、加速应用部署及维护提供了强大支持&#xff0c;但同时也给网络安全带来…

bugfix: com.alibaba.druid.sql.parser.EOFParserException: EOF

前言 在日常的开发工作中&#xff0c;我们经常会遇到各种各样的问题&#xff0c;其中涉及数据库操作的接口联调尤其容易出现意想不到的状况。今天我就遇到了一个关于Druid SQL解析异常的问题&#xff0c;具体表现为com.alibaba.druid.sql.parser.EOFParserException: EOF。通过…

42. UE5 RPG 实现火球术伤害

上一篇&#xff0c;我们解决了火球术于物体碰撞的问题&#xff0c;现在火球术能够正确的和攻击目标产生碰撞。接下来&#xff0c;我们要实现火球术的伤害功能&#xff0c;在火球术击中目标后&#xff0c;给目标造成伤害。 实现伤害功能的思路是给技能一个GameplayEffect&#x…

akSmart大带宽服务器基础配置科普

在数字化时代&#xff0c;服务器的性能和网络带宽成为业务发展的关键因素。RakSmart作为知名的服务器提供商&#xff0c;其大带宽服务器备受用户青睐。那么&#xff0c;RakSmart大带宽服务器的基础配置究竟有哪些呢?本文将为您揭开这一神秘面纱。 首先&#xff0c;我们来看看R…

阿里云X魔搭社区Create@AI创客松第四届冠军:MumuLab

4月13日终于迎来了线下Demo Day&#xff0c;此前阿里云 X 魔搭社区 X Datawhale CreateAI创客松已经紧锣密鼓地准备了一个多月时间&#xff0c;全球150团队报名、创作出66作品、评选出25支团队进入决赛&#xff0c;作品范围覆盖从办公效率到法律调解再到游戏互动以及构建童话世…

添加阿里云yum源

添加阿里云yum源 要添加阿里云的 yum 源&#xff0c;可以执行以下步骤&#xff1a; 首先&#xff0c;备份你的现有 yum 源配置文件&#xff0c;以防止意外更改&#xff1a; sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup然后&#xf…

跟着Datawhale重学数据结构与算法(3)---排序算法

开源链接&#xff1a;【 教程地址 】【电子网站】 【写博客的目的是记录自己学习过程&#xff0c;方便自己复盘&#xff0c;专业课复习】 数组排序&#xff1a; #mermaid-svg-F3iLcKsVv8gcmqqC {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16p…

【java】equals()方法

什么是Object类 Object类是所有类的始祖&#xff0c;在Java中每个类都是由它扩展而来的。 equals()方法 在 Object 类当中&#xff0c;equals()的底层是“”&#xff0c;String 类将其重写。 和equals() 有什么区别 对于基本数据类型而言&#xff0c;比较值是否相同&#x…

污水处理设备运维注意事项有哪些

污水处理设备的运维是确保污水处理效率和处理质量的关键环节。良好的运维不仅可以延长设备的使用寿命&#xff0c;还能确保污水处理过程的稳定性和可靠性。以下是一些污水处理设备运维的重要注意事项&#xff1a; 1. 定期检查和维护 设备检查&#xff1a;定期对污水处理设备进…

FFmpeg下载教程(Windows版)

文章目录 下载地址步骤 下载地址 https://ffmpeg.org/download.html 步骤

LeetCode53. 最大子数组和

LeetCode53. 最大子数组和 解题思路dp 代码 /* 数组长度n 9,连续的区间 那区间长度为1的区间数量是&#xff0c;9个 区间长度为2的区间数量是8个 区间长度为3的连续的区间数量为7个 .... 区间长度为9的区间数量为1个 */ class Solution { public:int maxSubArray(vector<…

文献速递:肺癌早期诊断---早期肺癌诊断:基于深度学习的循环外泌体光谱分析

Title 题目 Early-Stage Lung Cancer Diagnosis by Deep Learning-Based Spectroscopic Analysis of Circulating Exosomes 早期肺癌诊断&#xff1a;基于深度学习的循环外泌体光谱分析 Abstract 摘要 Lung cancer has a high mortality rate, but an early diagnosis can …

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

【STM32+HAL+Proteus】系列学习教程---ADC(查询、中断、DMA模式下的电压采集)

实现目标 1、学会STM32CubeMX软件关于ADC的配置 2、掌握ADC三种模式&#xff08;查询、中断、DMA&#xff09;编程 3、具体目标&#xff1a;1、将开发板单片机采集到的电压值上传至上位机串口调试助手显示。 一、ADC 概述 1、什么是ADC? ADC&#xff08;Analog to Digit…