目标
绘制一本图书,要求左右两侧带纸张厚度堆叠形成的弧形,带光影效果。

开始
1.创建基础容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 图书封面</title>
</head>
<body>
<div id="controls" style="background-color: transparent;"></div>
<script src="three.min.js"></script>
<script src="common.js"></script>
</body>
</html>2.初始化渲染器
const renderer = new THREE.WebGLRenderer({antialias: true, alpha: true}); // antialias: 抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器宽高
renderer.setClearColor('rgb(201,201,201)', 0.5) // 设置颜色及透明度
renderer.shadowMap.enabled = true; // 启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 使用软阴影类型3.创建画布
创建画布,并指定渲染器
const canvas = document.getElementById('controls');
canvas.appendChild(renderer.domElement);4.创建相机
创建相机,并指定相机参数
const fov = 60; // 摄像机视锥体垂直视野角度
const aspect = window.innerWidth / window.innerHeight; // 摄像机视锥体长宽比
const near = 1; // 摄像机视锥体近端面
const far = 1000; // 摄像机视锥体远端面
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 20;5.创建场景,并指定场景背景
const scene = new THREE.Scene();
scene.background = new THREE.Color('#ffffff');6.添加环境光
const ambientLight = new THREE.AmbientLight('#939393', 0.5);
scene.add(ambientLight);7.添加灯光
// color -(可选)一个表示颜色的 Color 的实例、字符串或数字,默认为一个白色(0xffffff)的 Color 对象。
// intensity -(可选)光照强度。默认值为 1。
// distance - 光源照射的最大距离。默认值为 0(无限远)。
// angle - 光线照射范围的角度。默认值为 Math.PI/3。
// penumbra - 聚光锥的半影衰减百分比。默认值为 0。
// decay - 沿着光照距离的衰减量。默认值为 2。
// const spotLight = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay);
const spotLight = new THREE.SpotLight('#FFFFFF', 2, 80, Math.PI / 6, 0, 0);
spotLight.castShadow = true; // 启用阴影
spotLight.position.set(10, 10, 10);
scene.add(spotLight);8.创建材质
创建一个材质,为了有更好的反光效果,这里使用MeshPhongMaterial材质
const material = new THREE.MeshPhongMaterial({color: '#4f67a2', side: THREE.DoubleSide});9.主体创建
创建一个3D矩形,作为基础书本主体。
const bookBody = new THREE.BoxGeometry(/*宽x*/4, /*高y*/6, /*厚度z*/0.5);
const bookBodyModel = new THREE.Mesh(bookBody, material);
bookBodyModel.position.y = 3
bookBodyModel.castShadow = true // 启用阴影
10.两侧厚度弧形
为了更加逼真的效果,创建厚度的弧形使用圆筒进行创建。
// 左侧外凸弧形
const bookRightBody = new THREE.CylinderGeometry(
/*圆柱的顶部半径*/0.9,
/*圆柱的底部半径*/0.9,
/*圆柱的高度*/6,
/*圆柱侧面周围的分段数*/64,
/*圆柱侧面沿着其高度的分段数*/5,
/*是否封顶*/true,
/*第一个分段的起始角度*/1.35,
/*圆柱底面圆扇区的中心角*/0.15 * Math.PI);
const bookRightModel = new THREE.Mesh(bookRightBody, material);
bookRightModel.position.y = 3
bookRightModel.position.x = -2.9
// // 右侧内凹弧形
const bookLeftBody = new THREE.CylinderGeometry(
/*圆柱的顶部半径*/1,
/*圆柱的底部半径*/1,
/*圆柱的高度*/6,
/*圆柱侧面周围的分段数*/64,
/*圆柱侧面沿着其高度的分段数*/5,
/*是否封顶*/true,
/*第一个分段的起始角度*/1.35,
/*圆柱底面圆扇区的中心角*/0.15 * Math.PI);
const bookLeftModel = new THREE.Mesh(bookLeftBody, material);
bookLeftModel.position.y = 3
bookLeftModel.position.x = 1.03
11.创建几何图形组
为了后期多个几何体能够同步进行交互,需要将这三个几何图形连接成一个组
const group = new THREE.Group();
group.add(bookBodyModel);
group.add(bookLeftModel);
group.add(bookRightModel);
scene.add(group);12.创建阴影效果
const shadowGeometry = new THREE.PlaneGeometry(500, 500, 500, 500); // 阴影区域大小
const shadowMaterial = new THREE.ShadowMaterial({opacity: 0.5,color: '#ababab'}); // 半透明阴影材质
const shadowMesh = new THREE.Mesh(shadowGeometry, shadowMaterial);
shadowMesh.rotation.x = -Math.PI / 2; // 平面朝下
shadowMesh.position.y = 0; // 放置在书本下方
shadowMesh.receiveShadow = true; // 接收阴影
scene.add(shadowMesh);13.完成渲染
renderer.render(scene, camera);
// 逐帧缓动
function animate() {
requestAnimationFrame(animate);
group.rotation.y -= 0.01;
renderer.render(scene, camera);
}
animate();14.添加动作交互
添加动作交互,以便更好预览
addMouseDragging(renderer, scene)
addMouseWheel(renderer, camera)15.添加辅助线
为相机,场景,几何体,地面等添加辅助线,以帮助更好掌握视角光线等信息。
成品样例
本文完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 图书封面</title>
</head>
<body>
<div id="controls" style="background-color: transparent;">
</div>
<script src="three.min.js"></script>
<script src="common.js"></script>
<script>
const canvas = document.getElementById('controls');
const renderer = new THREE.WebGLRenderer({antialias: true, alpha: true}); // antialias: 抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器宽高
renderer.setClearColor('rgb(201,201,201)', 0.5) // 设置颜色及透明度
renderer.shadowMap.enabled = true; // 启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 使用软阴影类型
canvas.appendChild(renderer.domElement);
const fov = 60; // 摄像机视锥体垂直视野角度
const aspect = window.innerWidth / window.innerHeight; // 摄像机视锥体长宽比
const near = 1; // 摄像机视锥体近端面
const far = 1000; // 摄像机视锥体远端面
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 20;
const scene = new THREE.Scene();
scene.background = new THREE.Color('#ffffff');
const helper = new THREE.CameraHelper(camera);
scene.add(helper);
const gridHelper = new THREE.GridHelper(/*坐标格尺寸*/10, /*坐标格细分次数*/10);
scene.add(gridHelper);
// 添加环境光
const ambientLight = new THREE.AmbientLight('#939393', 0.5);
scene.add(ambientLight);
// 添加聚光灯
// color -(可选)一个表示颜色的 Color 的实例、字符串或数字,默认为一个白色(0xffffff)的 Color 对象。
// intensity -(可选)光照强度。默认值为 1。
// distance - 光源照射的最大距离。默认值为 0(无限远)。
// angle - 光线照射范围的角度。默认值为 Math.PI/3。
// penumbra - 聚光锥的半影衰减百分比。默认值为 0。
// decay - 沿着光照距离的衰减量。默认值为 2。
const spotLight = new THREE.SpotLight('#FFFFFF', 2, 80, Math.PI / 6, 0, 0);
spotLight.castShadow = true; // 启用阴影
spotLight.position.set(10, 10, 10);
// spotLight.shadow.mapSize.width = 500; // default
// spotLight.shadow.mapSize.height = 500; // default
// spotLight.shadow.camera.near = 0.5; // default
// spotLight.shadow.camera.far = 500 // default
// spotLight.shadow.focus = 0.5;
scene.add(spotLight);
const spotLightHelper = new THREE.SpotLightHelper(spotLight ,'#88fa29');
scene.add(spotLightHelper);
// 材质
const material = new THREE.MeshPhongMaterial({color: '#4f67a2', side: THREE.DoubleSide}); // greenish blue
const bookRightBody = new THREE.CylinderGeometry(
/*圆柱的顶部半径*/0.9,
/*圆柱的底部半径*/0.9,
/*圆柱的高度*/6,
/*圆柱侧面周围的分段数*/64,
/*圆柱侧面沿着其高度的分段数*/5,
/*是否封顶*/true,
/*第一个分段的起始角度*/1.35,
/*圆柱底面圆扇区的中心角*/0.15 * Math.PI);
const bookLeftBody = new THREE.CylinderGeometry(
/*圆柱的顶部半径*/1,
/*圆柱的底部半径*/1,
/*圆柱的高度*/6,
/*圆柱侧面周围的分段数*/64,
/*圆柱侧面沿着其高度的分段数*/5,
/*是否封顶*/true,
/*第一个分段的起始角度*/1.35,
/*圆柱底面圆扇区的中心角*/0.15 * Math.PI);
const bookBody = new THREE.BoxGeometry(/*宽x*/4, /*高y*/6, /*厚度z*/0.5);
const bookRightModel = new THREE.Mesh(bookRightBody, material);
bookRightModel.position.y = 3
bookRightModel.position.x = -2.9
const bookLeftModel = new THREE.Mesh(bookLeftBody, material);
bookLeftModel.position.y = 3
bookLeftModel.position.x = 1.03
const bookBodyModel = new THREE.Mesh(bookBody, material);
bookBodyModel.position.y = 3
bookBodyModel.castShadow = true
// bookBodyModel.position.x = 3
const axes = new THREE.AxesHelper();
axes.material.depthTest = false;
axes.renderOrder = 1;
bookBodyModel.add(axes)
const group = new THREE.Group();
group.add(bookBodyModel);
group.add(bookLeftModel);
group.add(bookRightModel);
scene.add(group);
const shadowGeometry = new THREE.PlaneGeometry(500, 500, 500, 500); // 阴影区域大小
const shadowMaterial = new THREE.ShadowMaterial({opacity: 0.5,color: '#ababab'}); // 半透明阴影材质
const shadowMesh = new THREE.Mesh(shadowGeometry, shadowMaterial);
shadowMesh.rotation.x = -Math.PI / 2; // 平面朝下
shadowMesh.position.y = 0; // 放置在书本下方
shadowMesh.receiveShadow = true; // 接收阴影
scene.add(shadowMesh);
renderer.render(scene, camera);
function animate() {
requestAnimationFrame(animate);
group.rotation.y -= 0.01;
renderer.render(scene, camera);
}
animate();
// 添加交互
addMouseDragging(renderer, scene)
addMouseWheel(renderer, camera)
</script>
</body>
</html>作者:yuanfun 创建时间:2024-12-30 08:58
最后编辑:yuanfun 更新时间:2025-01-14 17:00
最后编辑:yuanfun 更新时间:2025-01-14 17:00