flutter3d_stereo 0.8.0 copy "flutter3d_stereo: ^0.8.0" to clipboard
flutter3d_stereo: ^0.8.0 copied to clipboard

PlatformAndroid

Stereo rendering for flutter3d: a rig of two eyes under a head, the widget that draws the pair into one frame, and the settings a pair can actually have.

flutter3d_stereo #

Stereo for this engine: a rig of two eyes, the widget that draws them side by side into one frame, and a head to point them with.

final rig = StereoRig();          // two cameras 64 mm apart, under a head
scene.add(rig.stage);             // the only node an application moves

StereoSurface(
  renderer: renderer,
  scene: scene,
  rig: rig,
  onBeforeFrame: () => rig.applyHead(tracker.pose.value),
  settings: () => const RenderSettings(exposure: 1.2),
);

What is here, and what is deliberately not #

The package holds what a phone and a headset agree about: two cameras a fixed distance apart, two views into one target, a set of render settings a pair can actually have, a pose that comes from outside, and the handful of numbers that describe a holder. None of it mentions OpenXR, and none of it needs a device to be right. All of it can be got wrong long before there is a runtime to blame.

Three things wait until a headset has answered: the swapchain, the frame's timing, and a pose predicted by a compositor rather than reported by a sensor. Those change who drives the frame, and an interface guessed before the device exists would describe the wrong machine.

Lens distortion is missing for a plainer reason. A lens bends straight lines outward. Undoing that means drawing the frame into a texture and sampling it back through the inverse curve, which is a post-processing pass written once per shader dialect across four backends. Until that pass exists, a wide-angle holder shows straight edges bowed near the rim of the picture. The geometry below is right; the rim is not.

The four parts #

StereoRig stage → head → two eyes. The head is what the tracker says. The stage is where the player stands, and it is the only thing an application moves. Walking, riding, or starting somewhere other than the origin all happen on the stage, because overriding the sensor sixty times a second does not make a locomotion system.
StereoSurface SceneSurface for a pair. It renders two views into one frame and applies RenderSettings.forStereo itself instead of relying on the caller to remember.
StereoViewer the holder, described by the numbers holders differ in: how far apart the lenses are, how far the screen sits from them, how high the lens axis is above the tray, and how wide a window the lens allows. cardboardV1 and cardboardV2 are the published profiles; a holder that states its own figures is written out instead.
HeadTracker where the head is. SensorHeadTracker reads the phone's rotation vector; a headset's runtime replaces it without the other two noticing.

The holder, and why a centred pair is wrong in one #

StereoSurface(
  renderer: renderer,
  scene: scene,
  rig: rig,
  viewer: StereoViewer.cardboardV2,
  screen: const StereoScreen(width: 0.147, height: 0.068),   // metres
  onBeforeFrame: () => rig.applyHead(tracker.pose.value),
  settings: () => const RenderSettings(exposure: 1.2),
);

The lens does not sit in the middle of the half of the screen it looks at. Two lenses are a face's width apart and a phone is wider than that, so each eye reaches further towards the outside of its half than towards the inside. If both halves get the same centred frustum, the picture lands off the lens axis by that difference. The eyes then disagree about where things are, and the person wearing it reports a headache instead of a bug.

StereoScreen is in metres, because the arithmetic is a ratio between two lengths. If it is left out, the surface estimates it from its own size at 160 logical pixels to the inch. That is close enough to try a holder with, but the real figure is better. Take care with trayToLensHeight: here it is measured to the bottom edge of the screen, while a published profile measures to the bottom of the phone, so subtract the bezel before passing it on.

Without a viewer nothing changes. The frustums stay centred and as wide as half the surface, which is what a phone held in the hands wants.

Why the settings are not the caller's to choose #

Ambient occlusion and screen-space reflections are compiled for the first view and then applied to the whole frame. On a pair drawn side by side they reconstruct the right eye with the left eye's camera, which puts occlusion in the wrong places and reflections along the wrong ray on half the picture. Bloom blurs across the seam, so a bright edge in one eye glows into the other. On a pair all three effects are wrong, not merely slow. A caller could forget to turn them off, so StereoSurface does it.

Fog survives, because it is applied per draw with the view's own camera. Auto exposure survives too, on purpose: one exposure for two eyes is better than two that disagree while the head turns.

Where the tracker works, and where it does not #

SensorHeadTracker is an Android plugin. On desktop, on the web and on iOS there is nothing behind its channel, so it reports that once and leaves the head where it is. The rig and the surface do not depend on it. A scene still draws in stereo, and an application that points the head itself (a replay, a test, a camera on rails) has no use for the sensor anyway.

Three degrees of freedom on a phone #

SensorHeadTracker reports rotation and nothing else, so leaning forward moves nothing. A neck model that invented a translation from a rotation would make the room appear to slide about.

The arithmetic between Android's frame and the engine's lives in headRotationFromSensor, in Dart, where a test can check it against numbers. The native side forwards the sensor's values and how far the screen is turned, and decides nothing. Three frames meet in that function and none of them is the engine's; that is one of the two traps this package has already hit. The other is documented on the same function: in vector_math, Quaternion.rotate applies the transpose of what asRotationMatrix builds, and the matrix is what a SceneNode uses.

The example #

example/ draws a room in stereo on whatever device is at hand, pointed by the device's own sensor. It is the acceptance tool for the three parts above on real hardware. It is not a VR demo, since VR needs the half that a phone does not have.

0
likes
160
points
422
downloads

Documentation

API reference

Publisher

verified publisherpleion.dev

Weekly Downloads

Stereo rendering for flutter3d: a rig of two eyes under a head, the widget that draws the pair into one frame, and the settings a pair can actually have.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#vr #xr #stereo #game-development

License

MIT (license)

Dependencies

flutter, flutter3d, flutter3d_app, flutter3d_sim, vector_math

More

Packages that depend on flutter3d_stereo

Packages that implement flutter3d_stereo