当前位置: 首页 > news >正文

【基础】Three.js中如何添加阴影(附案例代码)

1. three.js 开启阴影

  1. 渲染器开启阴影
  2. 光源支持阴影: 并不是每种光源都支持投射阴影,有三种光可以投射阴影,分别为DirectionalLight 定向光、 PointLight 点光源、SpotLight 聚光灯
  3. 让物体投射 / 接收阴影: 材质必须是 支持光照的材质(如 MeshStandardMaterial、MeshPhongMaterial 等),否则不会显示阴影。
    请添加图片描述

2. 完整代码

<template><div ref="threeRef" class="three-wrapper"></div>
</template>
<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { onMounted, ref, onBeforeUnmount } from "vue";
import { GUI } from "three/addons/libs/lil-gui.module.min.js";const threeRef = ref();
let renderer = null;
let scene = null;
let controls = null;
let gui = null;const init = () => {// 场景scene = new THREE.Scene();// 透视相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);// 渲染器renderer = new THREE.WebGLRenderer({antialias: true, // 抗锯齿});renderer.shadowMap.enabled = true; // ✨渲染器开启阴影renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器大小renderer.setPixelRatio(window.devicePixelRatio); // // 适应不同的设备屏幕threeRef.value.append(renderer.domElement); // 将渲染器添加到DOM中// 灯光const light = new THREE.DirectionalLight(0xffffff, 1);light.position.set(5, 10, 5);light.castShadow = true; // ✨灯光投射阴影scene.add(light);// 地面(接受阴影)const groundGeo = new THREE.PlaneGeometry(100, 100);const groundMat = new THREE.MeshStandardMaterial({color: 0x888888,});const ground = new THREE.Mesh(groundGeo, groundMat);ground.rotation.x = -Math.PI / 2;ground.position.y = 0;ground.receiveShadow = true; // ✨地面接收阴影scene.add(ground);// 球体const ballGeo = new THREE.SphereGeometry(1, 32, 32);const ballMat = new THREE.MeshStandardMaterial({// 材质必须是 支持光照的材质color: 0xff0000,});
// 有一天,我看了44次日落。含义是const ball = new THREE.Mesh(ballGeo, ballMat);ball.position.set(0, 1, 0);ball.castShadow = true; // 球体能投射阴影或者被投射阴影ball.receiveShadow = true;scene.add(ball);camera.position.set(0, 5, 10);camera.lookAt(ball.position); // 相机朝向球体function animate() {requestAnimationFrame(animate);const time = performance.now() * 0.001;ball.position.y = Math.abs(Math.sin(time)) * 2 + 1; // 让球体上下移动renderer.render(scene, camera); //执行渲染操作}animate();// 添加坐标轴辅助器const dirLightHelper = new THREE.DirectionalLightHelper(light, 1, 0xff0000); //参数1:光源,参数2:长度,参数3:颜色scene.add(dirLightHelper);// 在页面上生成调试面板,动态调整对象属性gui = new GUI();const lightFolder = gui.addFolder("DirectionalLight Position");lightFolder.add(light.position, "x", -20, 20);lightFolder.add(light.position, "y", 0, 20);lightFolder.add(light.position, "z", -20, 20);lightFolder.open();// 用于相机交互控制,响应鼠标/触摸事件,实现旋转、缩放、平移等,让用户操控视角。controls = new OrbitControls(camera, renderer.domElement); //轨道控制controls.addEventListener("change", function () {renderer.render(scene, camera);});//  窗口响应式window.onresize = function () {camera.aspect = window.innerWidth / window.innerHeight; // 更新相机纵横比camera.updateProjectionMatrix(); // 用新的纵横比重算投影矩阵renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器的大小};
};onMounted(() => {init();
});// 在WebGL中创建的资源(几何体、材质、贴图等)不会被JavaScript的垃圾回收器自动回收,需要手动释放
onBeforeUnmount(() => {// 清理gui调试面板gui?.destroy();// 清理控制器controls?.dispose();// 清理渲染器renderer?.dispose();// 清理场景中的资源scene?.traverse((obj) => {// 遍历场景中的所有对象if (obj.isMesh) {obj.geometry.dispose();if (obj.material.map) obj.material.map.dispose(); // 清理贴图obj.material.dispose();}});// 移除窗口resize事件监听器window.onresize = null;
});
</script>
<style scoped>
.three-wrapper {width: 100%;height: calc(100vh ); /* 调整高度以适应容器 */overflow: hidden;
}
</style>

为什么动画用 requestAnimationFrame 而不是 setInterval

  • 与屏幕刷新同步(更流畅):requestAnimationFrame 会在下一次屏幕重绘前调用,自动跟随显示器刷新率(60/120/144Hz),避免撕裂和抖动;setInterval 与重绘不同步,容易卡顿。
  • 掉帧自适应:requestAnimationFrame 的回调带精准时间戳,掉帧时可按真实间隔更新逻辑,减少时间漂移;setInterval 容易累积误差。
  • 后台节能:页面不可见时,浏览器会自动降频/暂停 requestAnimationFrame,节能且不浪费 GPU;setInterval 仍可能触发,白耗资源。
  • 浏览器优化友好:requestAnimationFrame 允许浏览器合并多次重绘、批量提交到 GPU,性能更好;setInterval 会打断优化。
  • Three.js/WebGL 推荐做法:图形渲染应使用 requestAnimationFrame 或 Three.js 的 renderer.setAnimationLoop(兼容 XR),以获得最佳表现。

🔍【基础】Three.js的零基础入门篇(附案例代码)
🔍【基础】Three.js中添加操作面板,GUI可视化调试(附案例代码)
🔍【基础】Three.js加载纹理贴图、加载外部gltf格式文件
🔍【基础】Three.js 自定义几何体和复制几何体

http://www.dtcms.com/a/360798.html

相关文章:

  • 基于SpringBoot的运动服装销售系统【2026最新】
  • 大型语言模型微调 内容预告(69)
  • 剧本杀小程序系统开发:重塑社交娱乐新生态
  • Trae x MCP:一键打造品牌专属高质量SVG封面
  • apipost 8.x 脚本循环调用接口
  • 9月1日
  • WhatsApp 漏洞与 Apple 零日漏洞一起被利用于间谍软件攻击
  • LangChain VectorStores核心:多向量数据库统一交互层与RAG存储中枢
  • 深度学习——速问速答
  • Java视觉跟踪入门:使用OpenCV实现实时对象追踪
  • Vue2存量项目国际化改造踩坑
  • pyside6小项目:进制转换器
  • 《架构师手记:SpringCloud整合Nacos实战·一》
  • 2.MySQL库的操作
  • Spark实现推荐系统中的相似度算法
  • 【LeetCode】19、删除链表的倒数第N个结点
  • P1803 凌乱的yyy / 线段覆盖
  • 802.11 和 802.1X
  • 计算机毕设选题:基于Python+Django的健康饮食管理系统设计【源码+文档+调试】
  • 网络原理——TCP/UDP/IP
  • 【面试场景题】如何快速判断几十亿个数中是否存在某个数
  • 【面试场景题】100M网络带宽能不能支撑QPS3000
  • (3dnr)多帧视频图像去噪 (一)
  • 第六章 Vue3 + Three.js 实现高质量全景图查看器:从基础到优化
  • 站在巨人的肩膀上:gRPC通过HTTP/2构建云原生时代的通信标准
  • Goframe 框架下HTTP反向代理并支持MCP所需的SSE协议的实现
  • 【深度学习基础】深度学习中的早停法:从理论到实践的全面解析
  • 【php反序列化字符串逃逸】
  • word运行时错误‘53’,文件未找到:MathPage.WLL,更改加载项路径完美解决
  • Android原生HttpURLConnection上传图片方案