Skip to content

Commit 751034a

Browse files
committed
Merge branch 'v4' into feature/remove-default-camera-warning
2 parents 294bc28 + f688c64 commit 751034a

File tree

20 files changed

+594
-55
lines changed

20 files changed

+594
-55
lines changed

docs/.vitepress/config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export default defineConfig({
8181
items: [
8282
{ text: 'Extending', link: '/advanced/extending' },
8383
{ text: 'primitive', link: '/advanced/primitive' },
84+
{ text: 'Performance', link: '/advanced/performance' },
8485
{
8586
text: 'Caveats',
8687
link: '/advanced/caveats',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
import { useTresContext } from '@tresjs/core'
3+
import { useGLTF } from '@tresjs/cientos'
4+
5+
const { nodes } = await useGLTF('https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/blender-cube.glb',
6+
{ draco: true })
7+
const model = nodes.Cube
8+
9+
model.position.set(0, 1, 0)
10+
11+
const state = useTresContext()
12+
13+
state.invalidate()
14+
</script>
15+
16+
<template>
17+
<primitive :object="model" />
18+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<script lang="ts" setup>
2+
import { ref } from 'vue'
3+
import { useRafFn } from '@vueuse/core'
4+
import { useState } from '../composables/state'
5+
6+
const width = 160
7+
const height = 40
8+
const strokeWidth = 2
9+
const updateInterval = 100 // Update interval in milliseconds
10+
const topOffset = 0 // Offset from the top
11+
12+
const points = ref('')
13+
const frameTimes = ref([])
14+
const maxFrames = ref(width / strokeWidth)
15+
16+
let lastUpdateTime = performance.now()
17+
18+
const { renderingTimes } = useState()
19+
20+
useRafFn(({ timestamp }) => {
21+
if (timestamp - lastUpdateTime >= updateInterval) {
22+
lastUpdateTime = timestamp
23+
24+
frameTimes.value.push(renderingTimes?.value)
25+
renderingTimes.value = 0
26+
27+
if (frameTimes.value.length > maxFrames.value) {
28+
frameTimes.value.shift()
29+
}
30+
31+
points.value = frameTimes.value
32+
.map(
33+
(value, index) =>
34+
`${index * strokeWidth},${
35+
height + topOffset - strokeWidth / 2 - (value * (height + topOffset - strokeWidth)) / 2
36+
}`,
37+
)
38+
.join(' ')
39+
}
40+
})
41+
</script>
42+
43+
<template>
44+
<div
45+
class="absolute
46+
right-2
47+
top-2
48+
flex
49+
px-4
50+
py-1
51+
justify-between
52+
gap-4
53+
items-center
54+
mb-2
55+
z-10
56+
bg-white
57+
dark:bg-dark
58+
shadow-xl
59+
rounded
60+
border-4
61+
border-solid
62+
bg-primary
63+
border-primary
64+
pointer-events-none
65+
overflow-hidden"
66+
>
67+
<label class="text-secondary text-xs w-1/3">Rendering Activity</label>
68+
69+
<div
70+
class="
71+
bg-gray-100
72+
dark:bg-gray-600
73+
relative
74+
w-2/3
75+
p-1
76+
rounded
77+
text-right
78+
text-xs
79+
focus:border-gray-200
80+
outline-none
81+
border-none
82+
font-sans
83+
"
84+
>
85+
<svg
86+
:width="width"
87+
:height="height"
88+
xmlns="http://www.w3.org/2000/svg"
89+
fill="none"
90+
>
91+
<polyline
92+
:points="points"
93+
stroke="lightgray"
94+
:stroke-width="strokeWidth"
95+
stroke-linecap="round"
96+
stroke-linejoin="round"
97+
/>
98+
</svg>
99+
</div>
100+
</div>
101+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup lang="ts">
2+
import { TresCanvas } from '@tresjs/core'
3+
import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
4+
import { OrbitControls } from '@tresjs/cientos'
5+
import { useState } from '../composables/state'
6+
import BlenderCube from './BlenderCube.vue'
7+
import GraphPane from './GraphPane.vue'
8+
import RenderingLogger from './RenderingLogger.vue'
9+
10+
const { renderingTimes } = useState()
11+
12+
function onRender() {
13+
renderingTimes.value = 1
14+
15+
}
16+
</script>
17+
18+
<template>
19+
<GraphPane />
20+
<TresCanvas
21+
render-mode="on-demand"
22+
clear-color="#82DBC5"
23+
@render="onRender"
24+
>
25+
<TresPerspectiveCamera
26+
:position="[5, 5, 5]"
27+
:look-at="[0, 0, 0]"
28+
/>
29+
<Suspense>
30+
<BlenderCube />
31+
</Suspense>
32+
<TresGridHelper />
33+
<RenderingLogger />
34+
<TresAmbientLight :intensity="1" />
35+
<TresDirectionalLight
36+
:position="[0, 8, 4]"
37+
:intensity="0.7"
38+
/>
39+
</TresCanvas>
40+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
import { useRenderLoop, useTresContext } from '@tresjs/core'
3+
import { OrbitControls } from '@tresjs/cientos'
4+
import { onMounted } from 'vue'
5+
import { useState } from '../composables/state'
6+
7+
const { renderingTimes } = useState()
8+
9+
const state = useTresContext()
10+
11+
function manualInvalidate() {
12+
state.invalidate()
13+
}
14+
15+
onMounted(() => {
16+
manualInvalidate()
17+
})
18+
</script>
19+
20+
<template>
21+
<OrbitControls
22+
@change="manualInvalidate"
23+
/>
24+
</template>
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { reactive, toRefs } from 'vue'
2+
3+
const state = reactive({
4+
renderingTimes: 0,
5+
})
6+
export function useState() {
7+
return {
8+
...toRefs(state),
9+
10+
}
11+
}

docs/advanced/performance.md

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Scaling performance 🚀
2+
3+
> Quick guide with tips to improve performance of your Tres.js application.
4+
5+
We are running WebGL on the browser, which can be quite expensive and it will depend on how powerful the user's device is. To make 3D accessible to everyone, we need to make sure our applications are optimized to run also on low-end devices. This guide will provide some tips to improve the performance of your Tres.js application.
6+
7+
## On-demand rendering <Badge type="tip" text="^4.0.0" />
8+
9+
By default, Tres.js will render your scene on every frame. This is great for most applications, but if you are building a game or a complex application, you might want to control when the scene is rendered.
10+
11+
Otherwise it might drain your device battery 🔋 🔜 🪫 and make your computer sound like an airplane 🛫.
12+
13+
Ideally, you only want to **render the scene when necessary**, for example when the user interacts with the scene and the camera moves, or when objects in the scene are animated.
14+
15+
You can do that by setting the `renderMode` prop to `on-demand` or `manual`:
16+
17+
18+
### Mode `on-demand`
19+
20+
<ClientOnly>
21+
<div style="position: relative; aspect-ratio: 16/9; height: auto; margin: 2rem 0; border-radius: 8px; overflow:hidden;">
22+
<onDemandRendering />
23+
</div>
24+
</ClientOnly>
25+
26+
27+
```vue
28+
<TresCanvas render-mode="on-demand">
29+
<!-- Your scene goes here -->
30+
</TresCanvas>
31+
```
32+
33+
#### Automatic Invalidation
34+
35+
When using `render-mode="on-demand"`, Tres.js will automatically invalidate the current frame by observing component props and lifecycle hooks like `onMounted` and `onUnmounted`. It will also invalidate the frame when resizing the window or changing any prop from the `<TresCanvas>` component like `clearColor` or `antialias`.
36+
37+
The code below updates TresMesh's position-x prop every second, triggering a new render.
38+
39+
```vue
40+
<script setup>
41+
import { ref } from 'vue'
42+
43+
const positionX = ref(0)
44+
45+
setTimeout(() => {
46+
positionX.value = 1
47+
}, 1000)
48+
</script>
49+
50+
<template>
51+
<TresCanvas render-mode="on-demand">
52+
<TresMesh :position-x="positionX">
53+
<TresBoxGeometry />
54+
<TresMeshBasicMaterial color="teal" />
55+
</TresMesh>
56+
</TresCanvas>
57+
</template>
58+
```
59+
60+
#### Manual Invalidation
61+
62+
Since it is not really possible to observe all the possible changes in your application, you can also manually invalidate the frame by calling the `invalidate()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext):
63+
64+
65+
::: code-group
66+
67+
```vue [App.vue]
68+
<script setup>
69+
import { TresCanvas } from '@tresjs/core'
70+
import Scene from './Scene.vue'
71+
</script>
72+
73+
<template>
74+
<TresCanvas
75+
render-mode="manual"
76+
>
77+
<Scene />
78+
</TresCanvas>
79+
</template>
80+
```
81+
82+
```vue [Scene.vue]
83+
<script setup>
84+
import { useTres } from '@tresjs/core'
85+
86+
const boxRef = ref()
87+
const { invalidate } = useTres()
88+
89+
watch(boxRef.value, () => {
90+
boxRef.value.position.x = 1
91+
invalidate()
92+
})
93+
</script>
94+
95+
<template>
96+
<TresMesh ref="boxRef">
97+
<TresBoxGeometry />
98+
<TresMeshBasicMaterial color="teal" />
99+
</TresMesh>
100+
</template>
101+
```
102+
103+
:::
104+
105+
### Mode `always`
106+
107+
In this rendering mode, Tres will continously render the scene on every frame. This is the default mode and the easiest to use, but it's also the most resource expensive one.
108+
109+
110+
### Mode `manual`
111+
112+
If you want to have full control of when the scene is rendered, you can set the `render-mode` prop to `manual`:
113+
114+
```vue
115+
<TresCanvas render-mode="manual">
116+
<!-- Your scene goes here -->
117+
</TresCanvas>
118+
```
119+
120+
In this mode, Tres will not render the scene automatically. You will need to call the `advance()` method from the [`useTresContext` composable](../api/composables.md#usetrescontext) to render the scene:
121+
122+
```vue
123+
<script setup>
124+
import { useTres } from '@tresjs/core'
125+
126+
const { advance } = useTres()
127+
128+
advance()
129+
</script>
130+
```
131+

docs/api/composables.md

+3
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,7 @@ const context = useTresContext()
233233
| **scene** | the [scene](https://threejs.org/docs/?q=sce#api/en/scenes/Scene). |
234234
| **setCameraActive** | a method to set a camera active |
235235
| **sizes** | contains width, height and aspect ratio of your canvas |
236+
| **invalidate** | a method to invalidate the render loop. This is only required if you set the `render-mode` prop to `on-demand`. |
237+
| **advance** | a method to advance the render loop. This is only required if you set the `render-mode` prop to `manual`. |
238+
236239

docs/api/tres-canvas.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ renderer.shadowMap.type = PCFSoftShadowMap
7777
| **clearColor** | The color the renderer will use to clear the canvas. | `#000000` |
7878
| **context** | This can be used to attach the renderer to an existing [RenderingContext](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext) | |
7979
| **depth** | Whether the drawing buffer has a [depth buffer](https://en.wikipedia.org/wiki/Z-buffering) of at least 16 bits. | `true` |
80+
| **renderMode** | Render mode, can be `always`, `on-demand` or `manual`. See [Performance](../advanced/performance) | `always` |
8081
| **disableRender** | Disable render on requestAnimationFrame, useful for PostProcessing | `false` |
8182
| **failIfMajorPerformanceCaveat** | Whether the renderer creation will fail upon low performance is detected. See [WebGL spec](https://registry.khronos.org/webgl/specs/latest/1.0/#5.2) for details. | `false` |
8283
| **logarithmicDepthBuffer** | Whether to use a logarithmic depth buffer. It may be necessary to use this if dealing with huge differences in scale in a single scene. Note that this setting uses gl_FragDepth if available which disables the [Early Fragment Test](https://www.khronos.org/opengl/wiki/Early_Fragment_Test) optimization and can cause a decrease in performance. | `false` |
8384
| **outputColorSpace** | Defines the output encoding | `LinearEncoding` |
84-
| **powerPreference** | Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context. Can be "high-performance", "low-power" or "default". | `default` |
85-
| **precision** | Shader precision. Can be "highp", "mediump" or "lowp". | "highp" if supported by the device |
85+
| **powerPreference** | Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context. Can be `high-performance`, `low-power` or `default`. | `default` |
86+
| **precision** | Shader precision. Can be `highp`, `mediump` or `lowp`. | "highp" if supported by the device |
8687
| **premultipliedAlpha** | Whether the renderer will assume that colors have [premultiplied alpha](https://en.wikipedia.org/wiki/Glossary_of_computer_graphics#premultiplied_alpha). | `true` |
8788
| **preserveDrawingBuffer** | Whether to preserve the buffers until manually cleared or overwritten.. | `false` |
8889
| **shadows** | Enable shadows in the renderer | `false` |

docs/components.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ export {}
77

88
declare module 'vue' {
99
export interface GlobalComponents {
10+
BlenderCube: typeof import('./.vitepress/theme/components/BlenderCube.vue')['default']
1011
DonutExample: typeof import('./.vitepress/theme/components/DonutExample.vue')['default']
1112
EmbedExperiment: typeof import('./.vitepress/theme/components/EmbedExperiment.vue')['default']
1213
ExtendExample: typeof import('./.vitepress/theme/components/ExtendExample.vue')['default']
1314
FirstScene: typeof import('./.vitepress/theme/components/FirstScene.vue')['default']
1415
FirstSceneLightToon: typeof import('./.vitepress/theme/components/FirstSceneLightToon.vue')['default']
16+
GraphPane: typeof import('./.vitepress/theme/components/GraphPane.vue')['default']
1517
HomeSponsors: typeof import('./.vitepress/theme/components/HomeSponsors.vue')['default']
1618
LocalOrbitControls: typeof import('./.vitepress/theme/components/LocalOrbitControls.vue')['default']
1719
LoveVueThreeJS: typeof import('./.vitepress/theme/components/LoveVueThreeJS.vue')['default']
20+
OnDemandRendering: typeof import('./.vitepress/theme/components/OnDemandRendering.vue')['default']
21+
RenderingLogger: typeof import('./.vitepress/theme/components/RenderingLogger.vue')['default']
1822
RouterLink: typeof import('vue-router')['RouterLink']
1923
RouterView: typeof import('vue-router')['RouterView']
2024
SandboxDemo: typeof import('./.vitepress/theme/components/SandboxDemo.vue')['default']

docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"preview": "vitepress preview"
1010
},
1111
"dependencies": {
12-
"@tresjs/core": "workspace:*"
12+
"@tresjs/core": "workspace:^"
1313
},
1414
"devDependencies": {
1515
"unocss": "^0.58.3",

0 commit comments

Comments
 (0)