目标

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

样例

开始

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
上一篇:
下一篇: