Skip to content

Commit

Permalink
Add FrameTime and FrameCount; prevent multiple unit updates per f…
Browse files Browse the repository at this point in the history
…rame (#373)
  • Loading branch information
hmans authored Dec 1, 2022
1 parent a3356db commit 1e313b5
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 3 deletions.
11 changes: 11 additions & 0 deletions .changeset/stale-melons-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@shader-composer/core": minor
---

Added two new units: `FrameTime` and `FrameCount`.

`FrameTime` is a unit that represents the time in seconds since the application started. Most importantly, this value is guaranteed to be stable across the duration of a frame, so it's perfect for synchronizing multiple shaders.

`FrameCount` provides an integer counter of the number of frames rendered since the start of the application. This, too, is great for synchronizing shaders, and might be better for when you need an auto-increasing integer value instead of a floating point time value.

If you need these values in your JavaScript `update` callbacks, you can import the new `frame` object and access its `time` and `count` properties.
4 changes: 2 additions & 2 deletions apps/vfx-composer-examples/src/examples/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { composable, modules } from "material-composer-r3f"
import { Mul, Time } from "shader-composer"
import { FrameTime, Mul } from "shader-composer"
import { Color } from "three"

export default function Playground() {
Expand All @@ -8,7 +8,7 @@ export default function Playground() {
<mesh>
<sphereGeometry />
<composable.meshStandardMaterial>
<modules.Color color={Mul(new Color("hotpink"), Time())} />
<modules.Color color={Mul(new Color("hotpink"), FrameTime)} />
</composable.meshStandardMaterial>
</mesh>
</group>
Expand Down
3 changes: 2 additions & 1 deletion packages/shader-composer-core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DEBUG } from "./debug"
import { Expression } from "./expressions"
import { glslRepresentation } from "./glslRepresentation"
import { isSnippet, renameSnippet, Snippet } from "./snippets"
import { frame } from "./ticker"
import { collectFromTree, Item, walkTree } from "./tree"
import { isUnit, isUnitInProgram, Program, uniformName, Unit } from "./units"
import {
Expand Down Expand Up @@ -246,7 +247,7 @@ export const compileShader = (root: Unit) => {
STEP 6: Build per-frame update function.
*/
const update = (dt: number, payload?: any) => {
const now = performance.now()
const now = frame.time

for (const unit of unitsWithUpdates) {
const state = unit._unitState
Expand Down
1 change: 1 addition & 0 deletions packages/shader-composer-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from "./expressions"
export * from "./glslType"
export * from "./snippets"
export * from "./stdlib"
export * from "./ticker"
export * from "./tree"
export * from "./units"
28 changes: 28 additions & 0 deletions packages/shader-composer-core/src/stdlib/variables.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { $, Expression } from "../expressions"
import { frame } from "../ticker"
import { GLSLType, injectAPI, JSTypes, Unit, UnitConfig } from "../units"
import { $localToViewSpace, $localToWorldSpace } from "./spaces"
import { Bool, Int, Mat4, Vec2, Vec3 } from "./values"
Expand Down Expand Up @@ -181,6 +182,33 @@ export const Time = (initial: number = 0) => {
*/
export const GlobalTime = Time()

/**
* An absolute time value, in seconds, that remains stable during the duration
* of the currently rendered frame. This is useful for creating effects that
* require accurate synchronization across multiple shaders.
*/
export const FrameTime = UniformUnit("float", frame.time, {
name: "Frame Time",

update: () => {
FrameTime.value = frame.time
}
})

/**
* An absolute count of the number of frames that have been rendered so far.
* This is useful for creating effects that require accurate synchronization
* across multiple shaders, and prefer to use an integer frame count instead
* of a floating point time value like {@link FrameTime} provides.
*/
export const FrameCount = UniformUnit("int", frame.count, {
name: "Frame Count",

update: () => {
FrameCount.value = frame.count
}
})

export const Resolution = UniformUnit(
"vec2",
{ x: 0, y: 0 },
Expand Down
24 changes: 24 additions & 0 deletions packages/shader-composer-core/src/ticker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* An object containing information about the current frame.
*/
export const frame = {
/**
* The current frame time, in seconds. This value does not change
* over the course of a single frame.
*/
time: performance.now() / 1000,

/**
* The current frame count.
*/
count: 0
}

function tick() {
requestAnimationFrame(tick)

frame.time = performance.now() / 1000
frame.count++
}

requestAnimationFrame(tick)

0 comments on commit 1e313b5

Please sign in to comment.