VRlizate
Español · English
VRlizate es un motor 3D/VR Open Source escrito en Dart para crear experiencias
inmersivas accesibles desde smartphones y aplicaciones Flutter. El paquete
vrlizate está en la versión 1.11.0; el objetivo del ecosistema abierto es
ofrecer un núcleo verificable y extensible: un mismo
formato de escenas e interacción que pueda adaptarse desde teléfonos económicos
hasta dispositivos con seguimiento avanzado.
Estado: desarrollo activo. La disponibilidad de sensores y el rendimiento se verifican por dispositivo; las funciones opcionales requieren integración de la aplicación anfitriona.
Qué incluye 1.11.0
- Escenas 3D con perfiles Lite/Standard/High y consultas de sombras acotadas.
- Protocolo padre/hijo, transporte TCP local autenticado y controlador IMU 3DoF.
- Avatar del teléfono con distribución de botones y modelo de brazo estimado.
- Flecha de regreso 3D, mejoras al Home de ejemplo y reinicio correcto del dwell después de suprimirlo por otra entrada.
dependencies:
vrlizate: ^1.11.0
Este paquete contiene el núcleo y el renderer Dart/Canvas. La clase
VrlizateScene pertenece a ese núcleo; el paquete independiente
vrlizate_scene aporta el adaptador Flutter GPU. Los widgets para ese adaptador
se distribuyen por separado en vrlizate_widgets.
Principios
- Accesible: el nivel Lite prioriza 3DoF, gaze y dispositivos modestos.
- Progresivo: una experiencia puede añadir controles, iluminación y 6DoF cuando el hardware los soporte.
- Extensible: sensores y controles comunitarios implementan
VrInputDriversin acoplarse a una escena. - Predecible: el árbitro reutiliza eventos mediante un pool de capacidad fija. Esto no elimina las asignaciones del renderer, JSON o snapshots de red.
- Abierto: código Apache 2.0, decisiones documentadas y contribuciones verificadas por tests.
Consulta ARCHITECTURE.md para ver los niveles Lite, Standard y Pro, así como el flujo completo entre sensores, interacción y renderizado.
Requisitos
- Flutter compatible con Dart
>=3.10.8 <4.0.0. - Un dispositivo, simulador o plataforma de escritorio soportada por Flutter.
- Giroscopio para seguimiento de cabeza en un teléfono físico. Escritorio y web pueden utilizar sus entradas de respaldo.
Clonar y verificar
git clone https://github.com/Open-Neom/vrlizate.git
cd vrlizate
flutter pub get
flutter test
flutter analyze
Para ejecutar la aplicación de demostración:
cd example
flutter pub get
flutter run
Usa flutter devices para seleccionar explícitamente un teléfono o una
plataforma de escritorio.
Crear una experiencia mínima
Añade el paquete a tu pubspec.yaml y construye una escena con la API pública:
import 'dart:ui';
import 'package:vector_math/vector_math.dart';
import 'package:vrlizate/vrlizate.dart';
final engine = VREngine(
scene: VrlizateScene(
quality: VrlizateSceneQuality.standard,
rayTracingMode: VrlizateRayTracingMode.hybridObjectSpace,
),
);
void createExperience() {
engine.cameraRig.position = Vector3(0, 1.6, 3);
engine.scene.add(
LitMeshNode(
name: 'welcome-cube',
geometry: CubeGeometry(size: 1),
material: PBRMaterial(color: const Color(0xFF2E90FA)),
),
);
engine.enableHeadTracking();
engine.enableGazePointer(dwellDuration: 1.5);
engine.start();
}
La carpeta example/ contiene escenas más completas. Mantén la
lógica particular de tu experiencia fuera del núcleo y compón módulos mediante
las API públicas de escena, interacción, locomoción y entrada.
Calidad 3D y trazado de rayos móvil
VrlizateScene es la escena moderna recomendada. Conserva meshes reales,
materiales PBR, luces, niebla, UI espacial y culling. No debe confundirse con
la clase histórica VRScene, mantenida por compatibilidad con experiencias de
partículas 2D.
Los perfiles ajustan automáticamente la teselación y el máximo de consultas de
luz: lite desactiva las consultas, standard admite 18 y high 36 por frame.
El límite se puede sobrescribir después de medir el dispositivo. El modo
hybridObjectSpace sigue rasterizando los triángulos y lanza rayos CPU contra
AABB de los objetos más cercanos para estimar oclusión de luz directa. Produce
sombras espaciales estables en estéreo con un costo acotado, pero no ofrece
reflejos ni iluminación global de un path tracer por píxel.
En el demo, arrastra para mirar, camina en el sitio para acercarte a los paneles y toca o usa dwell para abrirlos. Doble toque recentra la vista. El Home, su barra inferior y las flechas de regreso viven dentro del mismo mundo 3D.
Crear un driver de entrada
VrInputArbiter aplica la prioridad remotePhone/touch > periféricos > gaze.
Una interacción aceptada se entrega síncronamente; un dwell de gaze queda
suprimido durante 400 ms después de la última actividad de mayor prioridad.
Cuando la aplicación gestiona GazePointer directamente, debe pasar
dwellEnabled: !arbiter.isGazeSuppressed a update. Así, al terminar la
supresión, el usuario dispone de un intervalo completo para seleccionar.
final class CommunityGamepadDriver implements VrInputDriver {
VrInputSink? _sink;
final Map<String, dynamic> _axisPayload = <String, dynamic>{
'x': 0.0,
'y': 0.0,
};
@override
VrInputSource get source => VrInputSource.gamepad;
@override
void attach(VrInputSink sink) {
_sink = sink;
// Suscríbete aquí a la API del gamepad.
}
void onAxis(double x, double y) {
final sink = _sink;
if (sink == null) return;
_axisPayload['x'] = x;
_axisPayload['y'] = y;
final event = sink.acquire(
type: VrInputType.navigate,
data: _axisPayload,
active: x.abs() > 0.1 || y.abs() > 0.1,
);
try {
sink.submit(event);
} finally {
sink.release(event);
}
}
@override
void detach() {
// Cancela aquí las suscripciones nativas.
_sink = null;
}
}
final arbiter = VrInputArbiter();
final gamepad = CommunityGamepadDriver();
arbiter.addListener((event) {
// El evento es memoria prestada: úsalo solo dentro de este callback.
});
arbiter.attachDriver(gamepad);
No conserves una referencia a un evento después del callback. Si necesitas historial, copia solamente los valores requeridos. Reutiliza también el payload del driver cuando este se emita cada frame.
Usar un segundo teléfono como control 3DoF
El visor puede abrir un socket local autenticado y compartir la URI resultante
como QR. La aplicación anfitriona debe obtener la IP LAN que el otro teléfono
pueda alcanzar; 0.0.0.0 sirve para enlazar el servidor, no para anunciarlo.
import 'dart:convert';
import 'dart:math';
final random = Random.secure();
final oneTimeRandomToken = base64UrlEncode(
List<int>.generate(24, (_) => random.nextInt(256)),
);
final transport = VrLocalSocketTransport();
final invitation = await transport.listen(
bindHost: '0.0.0.0',
advertisedHost: '192.168.1.20',
sessionToken: oneTimeRandomToken,
);
final arbiter = VrInputArbiter();
final session = VrRemoteControllerSession(
transport: transport,
arbiter: arbiter,
cameraRig: engine.cameraRig,
scene: engine.scene,
)..start();
// Codifica invitation.toUri() como QR en la capa de aplicación.
El teléfono controlador abre la URI y transmite su perfil e IMU:
final transport = VrLocalSocketTransport();
await transport.connect(VrPairingPayload.fromUri(scannedUri));
final controller = VrRemoteImuController(
transport: transport,
profile: VrControllerProfile(
deviceId: 'my-phone',
deviceName: 'Control derecho',
handedness: VrControllerHandedness.right,
widthMeters: 0.072,
heightMeters: 0.155,
controls: [
VrControlDescriptor(
id: 'select',
label: 'A',
kind: VrControlKind.button,
x: 0.75,
y: 0.75,
),
],
),
);
await controller.start();
VrRemoteControllerSession crea un teléfono 3D, ilumina los controles mediante
un bitset y usa VrControllerArmModel para estimar una posición cómoda relativa
a la cabeza. La rotación sí proviene del giroscopio; la posición visual es una
estimación acotada, no seguimiento 6DoF medido. El acelerómetro se transmite
pero nunca se integra como posición. Una distancia opcional debe incluir fuente
y confianza. El perfil describe los botones; la aplicación del control aporta
su interfaz táctil y configura la orientación de pantalla.
VrRemoteImuController se suscribe a sensors_plus y también admite streams
de sensores inyectados para pruebas o integraciones alternativas.
El socket de referencia funciona en plataformas dart:io y está pensado para
una LAN confiable. Autentica el token de sesión, pero no cifra el tráfico. Una
aplicación que atraviese redes no confiables debe proporcionar un transporte
seguro alternativo. El codec de referencia usa JSON, con snapshots que poseen
su memoria antes de una operación asíncrona. BLE y Wi-Fi Direct todavía
requieren implementar otro transporte; la ruta TCP no está disponible en web.
Para mantener la predicción actualizada entre paquetes, llama a
session.updatePosePrediction() antes de renderizar. En el cierre, detén la
sesión y libera sus recursos junto con el transporte y los sensores.
Módulos principales
lib/
├── core/ Cámara, motor, entrada, matemáticas, proyección y render
├── scene/ Grafo de escena, geometrías, materiales, luces y texturas
├── interaction/ Raycast, objetos interactivos y locomoción
├── spatial_ui/ Paneles, texto y botones espaciales
├── animation/ Clips, esqueletos, keyframes y skinning
├── physics/ Cuerpos rígidos y colisiones
├── effects/ Distorsión, niebla, bloom, SSAO y viñeta
├── loaders/ Carga de glTF/GLB
└── xr/ Integraciones XR
Contribuir
Lee CONTRIBUTING.md antes de abrir un cambio. Todo cambio de comportamiento debe incluir tests; las rutas ejecutadas cada frame deben evitar asignaciones accidentales y documentar sus límites de hardware.
Licencia
Apache License 2.0. Consulta LICENSE.
English
VRlizate is an open-source 3D/VR engine written in Dart for immersive
experiences on smartphones and Flutter applications. The vrlizate package is
at 1.11.0. Its goal is a testable, extensible core: one scene and interaction model that scales from
low-end phones to devices with advanced tracking.
Status: active development. Sensor availability and performance must be checked on each device; optional capabilities need host-app integration.
Version 1.11.0 adds the parent/child controller protocol and local TCP transport,
an estimated arm model and phone avatar, spatial back navigation, bounded hybrid
light queries, and corrected gaze dwell suppression. Add vrlizate: ^1.11.0 to
your dependencies and see CHANGELOG.md for the release notes.
This package uses the Dart/Canvas renderer. Its VrlizateScene class is distinct
from the separate vrlizate_scene Flutter GPU adapter package and that adapter's
vrlizate_widgets components.
Principles
- Accessible: Lite prioritizes 3DoF, gaze, and modest devices.
- Progressive: experiences can add controllers, lighting, and 6DoF when the hardware supports them.
- Extensible: community sensors and controllers implement
VrInputDriverwithout coupling themselves to a scene. - Predictable: the arbiter reuses fixed-pool events; JSON, network snapshots, and rendering still allocate memory.
- Open: Apache 2.0 code, documented decisions, and test-backed changes.
Read ARCHITECTURE.md for the Lite, Standard, and Pro tiers and the complete sensor-to-rendering data flow.
Requirements and verification
- Flutter with Dart
>=3.10.8 <4.0.0. - A Flutter-supported device, simulator, or desktop platform.
- A gyroscope for head tracking on a physical phone.
git clone https://github.com/Open-Neom/vrlizate.git
cd vrlizate
flutter pub get
flutter test
flutter analyze
Run the example application with:
cd example
flutter pub get
flutter run
The minimal scene and driver examples in the Spanish sections above use the same language-independent Dart API. Experience-specific logic should live outside the engine and compose the public scene, interaction, locomotion, and input modules.
3D quality and mobile ray tracing
Use VrlizateScene for modern experiences. It provides mesh geometry, PBR
materials, lighting, fog, spatial UI, culling, and hardware-aware quality
profiles. The legacy VRScene remains only for compatibility with the older
2D particle-style API.
hybridObjectSpace keeps triangle rasterization and spends a bounded CPU ray
budget on direct-light visibility against world AABBs. Defaults are zero rays
for lite, 18 for standard, and 36 for high; applications may override the
budget after profiling. This gives stable stereo object shadows without
claiming per-pixel path tracing, global illumination, or ray-traced reflections.
Adding an input driver
Implement VrInputDriver, keep the VrInputSink received by attach, and
cancel every hardware subscription in detach. For high-frequency values:
- Reuse payload storage.
- Call
sink.acquire. - Call
sink.submitinside atryblock. - Always call
sink.releaseinfinally. - Never retain an event delivered to an arbiter listener.
Second-phone 3DoF controller
VrLocalSocketTransport provides the native dart:io reference transport.
The visor calls listen, shares the resulting pairing URI, and connects a
VrRemoteControllerSession to its arbiter, camera, and scene. The child calls
connect and starts VrRemoteImuController with a VrControllerProfile.
The profile describes normalized visual control locations. Pose frames carry a
normalized quaternion, angular velocity, acceleration, button bitset, optional
touch, sequence number, and optional range with source/confidence. The Lite
avatar uses a bounded arm model to estimate a comfortable head-relative
position, not measured 6DoF tracking. Acceleration is never integrated into
translation. The host app provides the controller UI and screen-axis settings;
VrRemoteImuController subscribes to sensors_plus or injected sensor streams. The
reference socket authenticates but does not encrypt LAN traffic; untrusted
networks need a secure transport. The codec uses JSON and owned snapshots;
BLE/Wi-Fi Direct need separate implementations and TCP is unavailable on web.
Call session.updatePosePrediction() before rendering to refresh prediction
between packets, and dispose the session, transport, and sensors on exit.
VrInputArbiter enforces remotePhone/touch > peripherals > gaze. A gaze dwell
is suppressed for 400 ms after the last active higher-priority event, while
gaze hover remains available for reticle feedback.
Applications updating GazePointer themselves must pass
dwellEnabled: !arbiter.isGazeSuppressed to update so suppression clears the
timer and a full dwell interval is required when it ends.
Contributing and license
Read CONTRIBUTING.md before opening a change. Behavioral changes require tests, and per-frame paths must avoid accidental allocations. VRlizate is licensed under Apache License 2.0.
Libraries
- animation/animation_clip
- animation/animation_mixer
- animation/keyframe
- animation/skeleton
- animation/skin_deformer
- components/vr_element
- components/vr_scene
- core/camera/camera_rig
- core/camera/vr_camera
- core/engine/vr_engine
- core/input/background_isolate
- Cross-platform background isolate support.
- core/input/background_isolate_io
- Real background-isolate implementation using
dart:isolate(VM / native). - core/input/background_isolate_stub
- No-op stub for platforms without
dart:isolate(web / WASM). - core/input/controller_state
- core/input/desktop_input
- core/input/drivers/vr_gamepad_driver
- core/input/drivers/vr_laser_pointer_driver
- core/input/drivers/vr_remote_touchpad_driver
- core/input/gaze_pointer
- core/input/hand_state
- core/input/hand_tracking_driver
- core/input/head_tracker
- core/input/inertial_tap_detector
- core/input/input_fusion
- core/input/ultrasonic_gesture
- core/input/vr_input_arbiter
- core/input/vr_input_event_bus
- core/input/wifi_rtt
- core/input/wifi_sensing
- core/math/aabb
- core/math/transform3d
- core/network/vr_controller_protocol
- core/network/vr_controller_transport
- core/network/vr_local_socket_transport
- core/network/vr_local_socket_transport_io
- core/network/vr_local_socket_transport_stub
- core/network/vr_pairing_payload
- core/network/vr_remote_controller_session
- core/network/vr_remote_imu_controller
- core/network/vr_remote_pose_predictor
- core/network/vr_transport
- core/projection/stereoscopic_projection
- core/rendering/gpu_renderer
- core/rendering/render_pass
- core/rendering/shader_program
- core/rendering/vr_renderer
- debug/debug_overlay
- debug/grid_floor
- effects/bloom
- effects/device_params
- effects/distortion_mesh
- effects/fog
- effects/hybrid_ray_tracer
- effects/lens_distortion
- effects/ssao
- effects/vignette
- interaction/grabbable
- interaction/locomotion/fly
- interaction/locomotion/teleport
- interaction/locomotion/walk
- interaction/locomotion/walk_in_place
- interaction/pointable
- interaction/raycast
- loaders/gltf_parser
- physics/rigid_body
- scene/geometry
- scene/hologram_node
- scene/light
- scene/material
- scene/mesh
- scene/node
- scene/pbr_material
- scene/primitives/cube_geometry
- scene/primitives/cylinder_geometry
- scene/primitives/plane_geometry
- scene/primitives/sphere_geometry
- scene/scene
- scene/shadow
- scene/texture
- scene/vr_controller_arm_model
- scene/vr_controller_avatar_node
- scene/vrlizate_scene
- scene/wifi_radar_node
- spatial_ui/billboard
- spatial_ui/panel
- spatial_ui/spatial_text
- utils/frustum
- utils/vr_math
- vrlizate
- vrlizate — VR 3D engine for Flutter.
- xr/webxr_session
- WebXR session management abstraction. Provides the interface for future WebXR integration when Flutter web supports immersive sessions.