likes
comments
collection
share

react-three 实现3D游戏(3)——海洋、天空和云雾

作者站长头像
站长
· 阅读数 52

概述

在之前的章节中,我们的环境已经加载出来了,也添加了环境的音效,但是孤零零的小岛难免让人感到单调。这次我们来丰富下游戏世界的场景,添加一些细节。

海洋

关于水的纹理相关资料很多,我们直接使用官方的示例。

🔗 react-three的示例:Water shader 🔗 Three.js官网的示例:shader Ocean

react-three 实现3D游戏(3)——海洋、天空和云雾 我们直接从官网中获得海面的法线纹理贴图,把他放到public目录的textures文件夹中

react-three 实现3D游戏(3)——海洋、天空和云雾

在models下新建文件ocean.tsx,在这个文件中我们做3件事:

  1. 加载法线纹理贴图
  2. 配置水面的可选参数(这里照搬了示例中,你可以自行调试)
  3. 在帧渲染中,改变uniforms的时间值,让水面流动

附代码如下:

/**
  * 海洋组件
  * 参数:
  * range: 水面的范围
  */
extend({ Water });
export default function Ocean({ range }: { range: number }) {
  // 水面波纹的法线贴图
  const waterNormals = useTexture("/textures/waters.jpeg")
  waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping;

  const geometry = useMemo(() => new THREE.PlaneGeometry(range, range), [range]);
  const options: WaterOptions = useMemo(
    () => ({
      textureWidth: 512,
      textureHeight: 512,
      waterNormals, // 法线贴图
      sunDirection: new THREE.Vector3(),// 太阳的照射方向
      sunColor: 0xFFFACD, // 太阳的颜色
      // waterColor: 0x001e0f, // 水的颜色
      waterColor: "skyblue", // 水的颜色
      distortionScale: 3.7, // 水面扭曲指数
    }),
     waterNormals]
  );

  useFrame((state, delta) => {
    if (!state || !ref?.current) return;
    ref.current.material.uniforms.time.value += delta;
  });

  return (
    <water
      ref={ref}
      args={[geometry, options]}
      rotation-x={-Math.PI / 2}
      position={[0, -5.5, 0]}
    />
  );
}

这里如果组件water组件标红,需要在vite-env.d.ts中添加全局类型声明:

declare global {
  namespace JSX {
    interface IntrinsicElements {
      water: ReactThreeFiber.Object3DNode<Water, typeof Water>;
    }
  }
}

雾气

在react-three中添加雾气相当简单,只需要添加:<fog attach="fog" args={[0xfff0ea, 1, 200]} />代码到Canvas组件下,其中args的参数是雾效设置:

  • 雾的颜色:0xfff0ea,即十六进制颜色值 0xfff0ea,这个值代表着一种浅黄色。
  • 近截面(near):100,表示雾效的起始位置在相机的位置后1个单位处。
  • 远截面(far):200,表示雾效的结束位置在相机的位置后200个单位处。

天空

react-three 实现3D游戏(3)——海洋、天空和云雾 海洋有了,我们还需要一个更加逼真的天空。原来的渐变色背景,可以不动,我们为场景添加一个天空盒,Sky组件。

import { KeyboardControls, Sky } from '@react-three/drei'
<Canvas>
 <Sky
    distance={500}  // 天空盒的覆盖范围
    sunPosition={[20, 30, 10]}  // 太阳位置
    azimuth={180} //太阳水平方向位置
    inclination={180}  // 太阳倾斜角度,值越大太阳越高
    mieCoefficient={0.005}  // Mie 散射系数,影响天空的亮度和颜色
    mieDirectionalG={0.7}  // Mie 方向系数,影响天空的光散射角度
    rayleigh={3}  // Rayleigh 散射系数,影响天空的亮度和颜色
    turbidity={10}  // 浊度,影响天空的清晰度和颜色
  />
 </Canvas>

关于天空,可以参考threejs官网的示例:sky

通过它你可以更清楚的了解Sky组件各个参数的作用。 你可以自己调整这些参数,直到感觉适合你的项目。

如果你对Sky组件效果不满意,可以自己创建天空盒,最简单的方式用一个全景图作为纹理。创建一个球形geometry,将全景纹理双面渲染到球体上。

具体可以参考以下官方示例: 🔗 react-three 天空盒示例

云朵

我们使用react-three/drei中提供的Cloud组件

react-three 实现3D游戏(3)——海洋、天空和云雾

🔗 官网示例:react-three 云朵

注意,如果你不使用本地的纹理图片,该组件默认加载外网的在线纹理,但在中国大陆地区该纹理无法加载成功。

从示例中直接获取云朵的纹理图片,把它放到public文件下的textures文件夹中。

在models文件夹下新建clouds.tsx文件

import { Clouds, Cloud } from "@react-three/drei";
import * as THREE from "three";

/**
  * 云层
  * 参数:
  * num: 云朵的数量
  */
export default function CloudLayer({num=30}:{num?:number}) {
  return <Clouds material={THREE.MeshLambertMaterial} limit={200} range={200} texture="./textures/cloud.png" >
    <Cloud position={[0, 100, 0]} concentrate="random" growth={10} color="white" opacity={1.25} seed={0.3} bounds={[200, 1, 200]} volume={20} segments={num} />
  </Clouds>
}

这就是全部代码了,让我解释下Cloud的参数:

  • concentrate:用于管理云朵们的分布状态,有3个值:
    • random,随机分布
    • inside, 中心聚集
    • outside, 四周分布
  • growth:动画的速度的系数值
  • color:云朵的颜色
  • opacity: 透明度
  • seed: 随机种子,当随机种子一样时云朵形状一样。(默认随机取值)
  • bounds:边界,它的值构成一个长方体,云朵在这个长方体内分布。如上述值表示长200,宽200,高为1的长方体。
  • volume: 云的体积大小
  • segments: 云朵的数量

Clouds的参数:

  • limit:云朵数量的最大上限
  • range:云朵渲染的数量
  • texture:云的纹理

🔗 官方文档地址

挂载组件

在models文件夹下的index.tsx中挂载天空、雾气、海洋、云朵等环境。

import { Sky } from '@react-three/drei'
import Ocean from './ocean';
import CloudLayer from './cloudLayer';
...
  <Suspense fallback={null}>
      <Sky distance={500} sunPosition={[20, 30, 10]} />
      <fog attach="fog" args={[0xfff0ea, 1, 200]} />
      <Ocean range={500} />
      <CloudLayer />
...

react-three 实现3D游戏(3)——海洋、天空和云雾

结语

本次完成

  • 添加海洋、雾气
  • 添加天空、云层

最终效果

耳边响起舒缓的音乐,伴随着浪涛声,伫立欣赏远处天空的云朵,真让人有种心旷神怡之感啊!

水流心不惊,云在意俱迟。 一心不赘物,古今自逍遥。

react-three 实现3D游戏(3)——海洋、天空和云雾

项目地址

结语

后续计划添加一个用于显示玩家位置的小地图。 如果大家有什么想法的话,欢迎在评论区提出你的宝贵想法和意见。

转载自:https://juejin.cn/post/7362547962605174821
评论
请登录