threejs视频教程学习(5):水天一色小岛

这篇博客详细介绍了如何使用Three.js库创建3D场景,包括设置透视相机、渲染器、轨道控制器,以及添加天空、水面、小岛等元素。博主提供了创建天空球、添加视频纹理、水面反射和小岛模型的代码示例,并讨论了环境纹理和光照对场景的影响。此外,还涉及到了水面效果和视频纹理的应用,以及模型加载和环境光的设置。
摘要由CSDN通过智能技术生成

前言

内容来源:老陈threejs入门教程

陈老师讲的还是很不错的,贴图材料可以加视频里的qq群,或者用我下载的这个(微云):https://share.weiyun.com/6uA7pnYc

基础内容

<template>
    <div class="container" id="container">
    </div>
</template>

<script setup lang="ts">
// 引入threejs
import * as THREE from 'three';
// 导入轨道控制器,模块化开发导入的是jsm不是js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { onMounted } from 'vue';

onMounted(() => {
    // 创建场景
    const scene = new THREE.Scene();
    // 创建透视相机,通过相机来观察
    const camera = new THREE.PerspectiveCamera(
        75, // 角度
        window.innerWidth / window.innerHeight, // 长宽比
        1, // 近端
        2000 // 远端
    );
    // 设置相机的位置
    camera.position.set(-50, 50, 130);
    // 设置摄像机视锥体的长宽比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 更新摄像头投影矩阵
    camera.updateProjectionMatrix();
    // 将相机添加到场景中
    scene.add(camera);
    // 初始化渲染器
    const renderer = new THREE.WebGLRenderer({
        antialias: true // 抗锯齿
    });
    // 设置渲染的尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 设置渲染的输出编码
    renderer.outputEncoding = THREE.sRGBEncoding;
    // 将wbgl渲染的canvas内容添加到dom元素中
    document.getElementById('container')?.appendChild(renderer.domElement);
    // 使用渲染器,通过相机将场景渲染进来
    renderer.render(scene, camera);
    // 监听平面大小的改变,修改相机的比例、渲染器的宽高
    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    });
    // 创建轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置控制器阻尼,让物体拥有惯性,必须在动画循环里调用update()
    controls.enableDamping = true;
    // 添加坐标辅助器
    const axesHelper = new THREE.AxesHelper(10);
    scene.add(axesHelper);
    // 创建一个渲染函数,当场景发生变化后重新渲染
    const render = () => {
        controls.update();
        renderer.render(scene, camera);
        // 使用浏览器自带的请求动画帧函数不断的进行渲染
        requestAnimationFrame(render);
    };

    render();
   /** ******************创建物体的相关逻辑开始**************************** */

 /** ********************创建物体的相关逻辑结束********************************/
});

</script>

天空

天空是通过创建一个大型的球体,然后添加天空贴图来实现的。

 // 创建纹理加载器
 const textureLoader = new THREE.TextureLoader();
 // 创建天空球
 const skyGeometry = new THREE.SphereGeometry(1000, 60, 60);
 const skyTexture = textureLoader.load('../../../../public/textures/sky.jpg'); // 天空纹理
 const skyMaterial = new THREE.MeshBasicMaterial({
     map: skyTexture // 颜色贴图
 });
 const sky = new THREE.Mesh(skyGeometry, skyMaterial);
 scene.add(sky);

在这里插入图片描述
将球体反转

将球体进行反转,使其球里面是亮的外面是黑的

// 反转球体,主要是反转z轴
skyGeometry.scale(1, 1, -1);

在这里插入图片描述
注: 天空贴图有点问题,不是完全的360度,会出现重合的问题
在这里插入图片描述
将天空贴图替换为视频纹理
图片中云是静态的,可以通过替换为视频纹理来解决这个问题。

 // 添加视频纹理
 const skyVideo = document.createElement('video');
 skyVideo.src = '../../../../public/textures/sky.mp4';
 skyVideo.loop = true; // 设置视频循环播放

 // 添加鼠标点击事件,当鼠标点击时开始播放视频
 window.addEventListener('click', () => {
     if (skyVideo.paused && skyVideo.readyState === 4) {
         skyVideo.play();
         // 更新材质
         skyMaterial.map = new THREE.VideoTexture(skyVideo);
         skyMaterial.map.needsUpdate = true;
     }
 });

在这里插入图片描述

水面和小岛

创建水面

// 导入水面
import { Water } from 'three/examples/jsm/objects/Water2';

 // 创建水面
 const waterGeometry = new THREE.CircleGeometry(300, 30);
 const water = new Water(waterGeometry, {
     // 材质宽高
     textureWidth: 1024,
     textureHeight: 1024,
     // 水面颜色
     color: 0xeeeeff,
     // 水面流动的方向
     flowDirection: new THREE.Vector2(1, 1),
     // 波纹大小
     scale: 1
 });
 // 水面旋转至水平
 water.rotation.x = -Math.PI / 2;
 // 添加水面
 scene.add(water);

感觉我这个水面乖乖的
在这里插入图片描述

添加小岛

// 导入gltf模型加载库
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 导入模型解压库
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

 // 添加小岛
 const loader = new GLTFLoader();
 const dracoLoader = new DRACOLoader();
 // 设置路径
 dracoLoader.setDecoderPath('../../../../public/draco/');
 // 设置加载器
 loader.setDRACOLoader(dracoLoader);
 loader.load('../../../../public/island2.glb', gltf => {
     scene.add(gltf.scene);
 });

在这里插入图片描述
场景是黑色的是因为我们没有设置环境纹理,没有环境反射的光,所以看不见场景。另外我们的天空贴图只是普通的图片,是无法反射光的,还需要导入hdr的环境纹理

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';

// 导入hdr环境纹理
const hdrLoader = new RGBELoader();
hdrLoader.loadAsync('../../../../public/050.hdr')
 .then((texture) => {
     // 设置纹理映射
     texture.mapping = THREE.EquirectangularReflectionMapping;
     // 设置环境背景和环境纹理
     scene.background = texture;
     scene.environment = texture;
 });

在这里插入图片描述

添加平行光
添加平行光,让模型更亮一点。如果只添加平行光不添加环境光也可以看见模型,但是平行光照射不到的地方,模型依然是黑色的,因此环境光还是很重要的。

 // 添加平行光
 const light = new THREE.DirectionalLight(0xffffff, 1);
 light.position.set(100, 100, 10);
 scene.add(light);

最终效果

总感觉怪怪的,好像是电脑渲染的问题吧,显得有点呆😳
在这里插入图片描述
完整代码

<template>
    <div class="container" id="container">
    </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue';
// 引入threejs
import * as THREE from 'three';
// 导入轨道控制器,模块化开发导入的是jsm不是js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 导入水面
import { Water } from 'three/examples/jsm/objects/Water2';
// 导入gltf模型加载库
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 导入模型解压库
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';

onMounted(() => {
    // 创建场景
    const scene = new THREE.Scene();
    // 创建透视相机,通过相机来观察
    const camera = new THREE.PerspectiveCamera(
        75, // 角度
        window.innerWidth / window.innerHeight, // 长宽比
        1, // 近端
        2000 // 远端
    );
    // 设置相机的位置
    camera.position.set(-50, 50, 130);
    // 设置摄像机视锥体的长宽比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 更新摄像头投影矩阵
    camera.updateProjectionMatrix();
    // 将相机添加到场景中
    scene.add(camera);
    // 初始化渲染器
    const renderer = new THREE.WebGLRenderer({
        antialias: true, // 抗锯齿
        logarithmicDepthBuffer: true // 对数深度缓冲区
    });
    // 设置渲染的尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 设置渲染的输出编码
    renderer.outputEncoding = THREE.sRGBEncoding;
    // 将wbgl渲染的canvas内容添加到dom元素中
    document.getElementById('container')?.appendChild(renderer.domElement);
    // 使用渲染器,通过相机将场景渲染进来
    renderer.render(scene, camera);
    // 监听平面大小的改变,修改相机的比例、渲染器的宽高
    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    });
    // 创建轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置控制器阻尼,让物体拥有惯性,必须在动画循环里调用update()
    controls.enableDamping = true;
    // 添加坐标辅助器
    // const axesHelper = new THREE.AxesHelper(100);
    // scene.add(axesHelper);

    // 创建一个渲染函数,当场景发生变化后重新渲染
    const render = () => {
        controls.update();
        renderer.render(scene, camera);
        // 使用浏览器自带的请求动画帧函数不断的进行渲染
        requestAnimationFrame(render);
    };
    render();

    /** ******************创建物体的相关逻辑开始**************************** */
    // 创建纹理加载器
    const textureLoader = new THREE.TextureLoader();
    // 创建天空球
    const skyGeometry = new THREE.SphereGeometry(1000, 60, 60);
    const skyTexture = textureLoader.load('../../../../public/textures/sky.jpg'); // 天空纹理
    const skyMaterial = new THREE.MeshBasicMaterial({
        map: skyTexture // 颜色贴图
    });
    // 反转球体,主要是反转z轴
    skyGeometry.scale(1, 1, -1);
    const sky = new THREE.Mesh(skyGeometry, skyMaterial);
    scene.add(sky);

    // 添加视频纹理
    const skyVideo = document.createElement('video');
    skyVideo.src = '../../../../public/textures/sky.mp4';
    skyVideo.loop = true; // 设置视频循环播放

    // 添加鼠标点击事件,当鼠标点击时开始播放视频
    window.addEventListener('click', () => {
        if (skyVideo.paused && skyVideo.readyState === 4) {
            skyVideo.play();
            // 更新材质
            const texture = new THREE.VideoTexture(skyVideo);
            skyMaterial.map = texture;
            skyMaterial.map.needsUpdate = true;
        }
    });

    // 导入hdr环境纹理
    const hdrLoader = new RGBELoader();
    hdrLoader.loadAsync('../../../../public/050.hdr')
        .then((texture) => {
            // 设置纹理映射
            texture.mapping = THREE.EquirectangularReflectionMapping;
            // 设置环境背景和环境纹理
            scene.background = texture;
            scene.environment = texture;
        });
    // 添加平行光
    const light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.set(100, 100, 10);
    scene.add(light);

    // 创建水面
    const waterGeometry = new THREE.CircleGeometry(300, 30);
    const water = new Water(waterGeometry, {
        // 材质宽高
        textureWidth: 1024,
        textureHeight: 1024,
        // 水面颜色
        color: 0xeeeeff,
        // 水面流动的方向
        flowDirection: new THREE.Vector2(1, 1),
        // 波纹大小
        scale: 1
    });
    // 水面旋转至水平
    water.rotation.x = -Math.PI / 2;
    // 调整水面的位置,使水面能够掩盖一部分模型
    water.position.y = 3;
    // 添加水面
    scene.add(water);

    // 添加小岛
    const loader = new GLTFLoader();
    const dracoLoader = new DRACOLoader();
    // 设置路径
    dracoLoader.setDecoderPath('../../../../public/draco/');
    // 设置加载器
    loader.setDRACOLoader(dracoLoader);
    loader.load('../../../../public/island2.glb', gltf => {
        scene.add(gltf.scene);
    });
    /** ********************创建物体的相关逻辑结束********************************/
});
</script>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值