从0搭建一个3D立体场景:光影与交互全解析

3D场景的搭建框架的三要素

一个基础框架的搭建:相机,场景,渲染器;camera生成投影矩阵和视点矩阵,scene类似一个舞台,用来放我们要渲染的对象,renderer则是将我们的对象画到canvas中。

复制代码
// 初始化相机、场景、渲染器等
const canvas = document.getElementById("webgl");
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 3000);
camera.position.set(100, 100, 100);
camera.lookAt(0, 0, 0);

// 创建场景
const scene = new THREE.Scene();

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

控制器(OrbitControls)

控制器用来实现交互过程的,通过这个我们就可以实现动画的手动旋转,不加就是固定角度

复制代码
// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();

创建渲染对象(mesh)

场景搭建好了,那么我们就开始要放进去我们要渲染的对象啦,这时候我们创建一个几何体,将它加入到场景中。

THREE.Mesh需要接收两个参数:几何体+材质

复制代码
const geometry = new THREE.BoxGeometry(10, 10, 10);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

一个简单的立体就可以展示了:

通过渲染,恭喜你,一个简单的demo就完成了

复制代码
renderer.render(scene, camera);

不够一般我们都会通过一个动画的函数来进行调用,让它动起来,通过下面这个函数,一个可交互的立方体就展示出来了。

复制代码
// 动画循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  //让立方体旋转起来
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}

animate();

光与阴影

想让一个物体具有真实的立体感,我们需要通过光面和阴影来进行一个完整的映射过程。

开启阴影

渲染阴影比较消耗性能,Three.js为了性能考虑,一般是关闭了阴影的效果,所以需要我们手动开启一下。

复制代码
//开启阴影支持
renderer.shadowMap.enabled = true
//设置阴影类型,PCFSoftShadowMap 是软阴影(边缘带有模糊效果),PCFShadowMap 是硬阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
//设置场景背景颜色 改为浅灰色 方便更好形成明暗对比
renderer.setClearColor(0x262626);

同时我们要保证我们的对象可以接收到阴影

复制代码
const cube = new THREE.Mesh(geometry, material);
// 设置为投射阴影:即该物体挡住光线的时候可以产生阴影
cube.castShadow = true;
// 设置为接收阴影:即其他物体的影子可以投射在它的身上
cube.receiveShadow = true;

因为没有光照的情况下我们只能看到一个黑影,只有光照的情况下,才可以正常的渲染

开启光亮:

通过光照、光照的方向以及灯光布局的方式让物体变的有阴影。

光源包含环境光和平行光。环境光是一种均匀照亮场景中的光,用来充当补光的作用;平行光模拟的是主光源(类似太阳),用来使物体产生阴影和棱角的。

复制代码
//布局灯光
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
//平行光:
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
sunLight.position.set(1, 1, 1);
scene.add(sunLight);
//开启主光源的阴影
sunLight.castShadow = true;

创建地面

要看到物体的阴影,我们不仅需要一个物体对象,而且需要地面来进行展示,所以需要渲染一个地面对象,渲染方式和创建几何体一样:

复制代码
const planeGeometry = new THREE.PlaneGeometry(100, 100);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x444444 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2; // 使平面水平
plane.position.y = -5; // 地面位置在y轴-5 地面下沉到物体下方
// 地面必须设置为"接收阴影"
plane.receiveShadow = true;
scene.add(plane);

一个带有影子的立体几何就做出来了

源码:

安装three,创建ts文件,引入即可

复制代码
// 一个3D立体的场景 需要灯光需要阴影才能呈现一个立体感
import "./style.css";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";


// 初始化相机、场景、渲染器等
const canvas = document.getElementById("webgl");
const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 3000);
camera.position.set(100, 100, 100);
camera.lookAt(0, 0, 0);

// 创建场景
const scene = new THREE.Scene();

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas: canvas });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();

//开启阴影支持
renderer.shadowMap.enabled = true
//设置阴影类型,PCFSoftShadowMap 是软阴影(边缘带有模糊效果),PCFShadowMap 是硬阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
//设置场景背景颜色,改为浅灰色,方便更好形成明暗对比
renderer.setClearColor(0x262626);

// 创建一个简单的立方体
const geometry = new THREE.BoxGeometry(10, 10, 10);
// MeshBasicMaterial 基础材质,没有光照效果,需要更改为MeshStandardMaterial
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
// 设置为投射阴影:即该物体挡住光线的时候可以产生阴影
cube.castShadow = true;
// 设置为接收阴影:即其他物体的影子可以投射在它的身上
cube.receiveShadow = true;
scene.add(cube);

//布局灯光
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
//平行光:
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
sunLight.position.set(1, 1, 1);
scene.add(sunLight);
//开启主光源的阴影
sunLight.castShadow = true;
// // 优化阴影的精细度
// // 默认的阴影贴图可能比较糊,这里将其分辨率调大一倍 (默认是 512x512)
// sunLight.shadow.mapSize.width = 1024;
// sunLight.shadow.mapSize.height = 1024;

//创建地面
const planeGeometry = new THREE.PlaneGeometry(100, 100);
const planeMaterial = new THREE.MeshStandardMaterial({ 
    color: 0x444444,
    roughness: 0.8, // 地面粗糙一点,不反光
    metalness: 0.0  
  });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2; // 使平面水平
plane.position.y = -10; // 地面位置在y轴-10 地面下沉到物体下方

// 地面必须设置为"接收阴影"
plane.receiveShadow = true;
scene.add(plane);


// 动画循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  //让立方体旋转起来
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}

animate();
相关推荐
Zldaisy3d1 小时前
总重仅28.9克!3D打印钛合金镜腿为运动AR眼镜电子元件腾出重量空间
3d·制造
Dovis(誓平步青云)1 小时前
一年1956亿行:智能电表场景下的时序数据存储实践
运维·前端·自动化·音视频·prometheus
兰亭妙微UI设计公司1 小时前
兰亭妙微分享:高手设计登录页的12种切入点
开发语言·前端·javascript
前端Baymax1 小时前
静默的 86 小时:全链 402 停摆与零告警的故障复盘
开发语言·前端·javascript
要开心吖ZSH1 小时前
定时任务从外部调度搬回本地 @Scheduled:一次连接池被打满的复盘与选型思考
前端·firefox
不停喝水1 小时前
【前端转全栈java速通课】 项目实战④12-14节 拦截器过滤器和全局配置-cors跨域设置-token体系-利用session做验证码
java·开发语言·前端·ai·容器
代码猎人1 小时前
怎么开发一个微信小程序
前端
console.log('npc')1 小时前
第 3 章 工程化与项目初始化
前端·人工智能·大模型
掘金者阿豪1 小时前
数据集成软件不能只看“同步成功”:聊聊金仓 KFS 和订单数据同步这件事
前端·后端