构建场景
本指南介绍配置 @playcanvas/react 项目并创建简单三维场景,帮助理解核心概念。
最终效果如下:
这里假设已了解 React;新用户可先阅读 React 官方文档。
我们逐步搭建,每一步都有代码和实时示例。
第一个场景
若尚未安装,请先按安装指南 创建 React 项目。
npm create playcanvas@latest my-first-3d-app -- -f react
先显示一个盒体。场景需要 Application、Scene 和 Camera,才能看到内容。
将 /src/App.tsx 替换为以下代码:
- 演示
- 代码
import { Application, Entity } from '@playcanvas/react'
import { Render, Camera } from '@playcanvas/react/components'
const Scene = () => {
return (<>
<Entity name='box'>
<Render type='box'/>
</Entity>
</>)
}
export const App = () => (
<Application>
<Entity name='camera' position={[0, 0, 5]}>
<Camera />
</Entity>
<Scene />
</Application>
)
运行或打开 Demo 标签后,可见中央方形。
代码做了什么?
Application 初始化 HTML canvas 和渲染上下文。独立 Scene 包含带 Render 的 Entity。
Scene 将待渲染内容组织在一起,虽非强制,但便于管理。Entity 只是具有位置、旋转和缩放的容器,本身无显示;Render 赋予三维渲染能力。
PlayCanvas 采用 ECS,通过向实体添加组件赋予功能。
Camera 挂到实体后,从其位置沿负 Z 渲染。位置设为 [0, 0, 5],与盒体保持距离,可修改观察效果。
添加灯光
通过 Environment 或 Light 提供光照,类型详见灯光。此处使用自然的环境照明和一盏平行光,后续用来增强落地感。
- 演示
- 代码
import { Application, Entity } from '@playcanvas/react'
import { Render, Camera, Light, Environment } from '@playcanvas/react/components'
import { useEnvAtlas } from '@playcanvas/react/hooks'
const Scene = () => {
// load the assets
const { asset: envAtlas } = useEnvAtlas('/assets/environment.png');
// if the asset hasn't loaded, don't render anything
if (!envAtlas) return null;
return (<>
<Environment envAtlas={envAtlas} showSkybox={false} />
<Entity name='directional-light' position={[0, .001, 0]}>
<Light type='directional' color="orange" />
</Entity>
<Render type='box'/>
</>)
}
export const App = () => (
<Application>
<Entity position={[0, 0, 5]}>
<Camera clearColor="#090707" fov={28} />
</Entity>
<Scene />
</Application>
)
运行后盒体出现明暗,下面解释新增概念。
加载资源
useEnvAtlas 从 URL 加载环境图集,再传入 Environment。库还提供不同资源 hooks,帮助管理加载和错误状态。
详情见资源加载。
添加组件
- Environment:环境照明。
- Light:场景灯光。
- Render:盒体渲染。
即加载环境图集传入 Environment,再添加 Light。
添加交互
使用 Script 挂载 CameraControls,让鼠标旋转摄像机观察盒体。Camera 还设置清屏颜色和视野角。
- 演示
- 代码
import { Application, Entity } from '@playcanvas/react'
import { Render, Camera, Light, Environment } from '@playcanvas/react/components'
import { useEnvAtlas } from '@playcanvas/react/hooks'
import { Script } from '@playcanvas/react/components'
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs'
const Scene = () => {
// load the assets
const { asset: envAtlas } = useEnvAtlas('/assets/environment.png');
// if the asset hasn't loaded, don't render anything
if (!envAtlas) return null;
return (<>
<Environment envAtlas={envAtlas} />
<Entity name='directional-light' position={[0, .001, 0]}>
<Light type='directional' />
</Entity>
<Render type='box'/>
</>)
}
export const App = () => (
<Application>
<Entity name='camera' position={[4, 1, 4]}>
<Camera clearColor='#090707' fov={28} />
<Script script={CameraControls}/>
</Entity>
<Scene />
</Application>
)
也可直接给实体添加事件监听,或通过 Script 定义行为。
使用 Script 组件
Script 将自定义行为挂到 Entity,接入引擎更新循环,在 React 渲染之外逐帧运行,适合输入和动画。
比每帧重新渲染 React 树更高效,但脚本可能覆盖 Entity props,需要注意。
运行后可用鼠标环绕盒体,更多内容详见脚本编程。
添加三维模型
将盒体换为真实模型,可使用引擎支持的格式。Fab 提供许多免费模型。
- 演示
- 代码
import { Application, Entity } from '@playcanvas/react'
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs'
import { Render, Environment, Script, Light, Camera } from '@playcanvas/react/components'
import { useModel, useEnvAtlas } from '@playcanvas/react/hooks'
const Scene = () => {
// load the assets
const { asset: envAtlas } = useEnvAtlas('/assets/environment.png');
const { asset: lambo } = useModel('/assets/lambo.glb');
// if the assets haven't loaded, don't render anything
if (!lambo || !envAtlas) return null;
return <>
<Render type='asset' asset={lambo} />
<Entity name='directional-light' position={[0, .001, 0]}>
<Light type='directional' />
</Entity>
<Environment envAtlas={envAtlas} showSkybox={false}/>
</>
}
export const App = () => (
<Application>
<Entity name='camera' position={[4, 1, 4]}>
<Camera clearColor='#090707' fov={28} />
<Script script={CameraControls}/>
</Entity>
<Scene />
</Application>
)
现在应看到 Lamborghini。Render 改为渲染资源,useModel 加载模型并传入。加载期间返回 null,也可替换为加载指示,详见资源加载。
布置场景
已有摄像机、可环绕模型与环境光,再添加地面和阴影,使用 ShadowCatcher 与 Grid 改善展示。
- 演示
- 代码
import { Application, Entity } from '@playcanvas/react'
import { Environment, Light, Render, Camera } from '@playcanvas/react/components'
import { useEnvAtlas, useModel } from '@playcanvas/react/hooks'
import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs'
// ↑ previous imports
import { Script } from '@playcanvas/react/components'
import { ShadowCatcher as ShadowCatcherScript } from 'playcanvas/scripts/esm/shadow-catcher.mjs'
import { Grid as GridScript } from 'playcanvas/scripts/esm/grid.mjs'
const Scene = () => {
// load the assets
const { asset: envAtlas } = useEnvAtlas('/assets/environment.png');
const { asset: lambo } = useModel('/assets/lambo.glb');
// if the assets haven't loaded, don't render anything
if (!lambo || !envAtlas) return null;
return <>
<Environment envAtlas={envAtlas} showSkybox={false} />
<Entity name='directional-light' position={[0, .001, 0]}>
<Light type='directional' />
</Entity>
<Render type='asset' asset={lambo} />
</>
}
export const App = () => (
<Application>
<Script script={ShadowCatcherScript}/>
<Entity name='grid' scale={[1000, 1, 1000]}>
<Script script={GridScript} />
</Entity>
<Entity name='camera' position={[4, 1, 4]}>
<Camera clearColor='#090707' fov={28} />
<Script script={CameraControls} enableFly={false} />
</Entity>
<Scene />
</Application>
)
实时示例
场景已完整,可加入自己的资源和脚本,详见资源加载。
const Scene = () => { // load the assets const { asset: envAtlas } = useEnvAtlas('/assets/environment.png'); const { asset: lambo } = useModel('/assets/lambo.glb'); // if the assets haven't loaded, don't render anything if (!lambo || !envAtlas) return null; return <Entity name='model' > <Render type='asset' asset={lambo} /> <Entity name='directional-light' position={[0, .001, 0]}> <Light type='directional' /> </Entity> <Environment envAtlas={envAtlas} showSkybox={false} /> </Entity> } const App = () => { return <Application> <ShadowCatcher width={5} depth={5} /> <Entity name='grid' scale={[1000, 1, 1000]}> <Script script={GridScript}/> </Entity> <Entity name='camera' position={[4, 1, 4]}> <Camera clearColor='#090707' fov={28} renderSceneColorMap={true} /> <Script script={CameraControls} enableFly={false}/> </Entity> <Scene /> </Application> } render(<App />)
后续步骤
掌握基础后,可以开始构建自己的场景,继续通过资源加载指南学习导入和脚本交互。
也可阅读概念指南,了解更多场景技术。