Qt 3D Marco de renderizado
El aspecto Qt 3D Render permite que el algoritmo de renderizado esté completamente basado en datos. La estructura de datos de control se conoce como framegraph. De forma similar a como el ECS (entity component system) de Qt 3D permite definir un llamado Scenegraph construyendo una escena a partir de un árbol de Entidades y Componentes, el framegraph es también una estructura de árbol pero utilizada para un propósito diferente. A saber, controlar cómo se renderiza la escena.
En el transcurso de la representación de un solo fotograma, un renderizador 3D probablemente cambiará de estado muchas veces. El número y la naturaleza de estos cambios de estado dependen no sólo de los materiales (shaders, geometría de malla, texturas y variables uniformes) que se encuentren en la escena, sino también del esquema de renderizado de alto nivel que se esté utilizando.
Por ejemplo, es muy diferente utilizar un esquema tradicional de renderizado simple hacia delante que un enfoque de renderizado diferido. Otras características como los reflejos, las sombras, las ventanas múltiples y los pases tempranos de z-fill cambian los estados que un renderizador necesita establecer en el transcurso de un fotograma y cuándo deben producirse esos cambios de estado.
A modo de comparación, el renderizador deQt Quick 2 scenegraph responsable de dibujar las escenas de Qt Quick 2 está programado en C++ para hacer cosas como el procesamiento por lotes de primitivas y el renderizado de elementos opacos seguido del renderizado de elementos transparentes. En el caso de Qt Quick 2 eso está perfectamente bien, ya que cubre todos los requisitos. Como se puede ver en algunos de los ejemplos anteriores, es probable que un renderizador tan rígido no sea lo suficientemente flexible para escenas 3D genéricas, dada la multitud de métodos de renderizado disponibles. O si se pudiera hacer un renderizador lo suficientemente flexible para cubrir todos estos casos, su rendimiento probablemente se resentiría por ser demasiado general. Por si fuera poco, cada vez se investigan más métodos de renderizado. Por tanto, necesitábamos un método que fuera flexible y ampliable, y que, al mismo tiempo, fuera fácil de usar y mantener. Así surgió el framegraph.
Cada nodo del framegraph define una parte de la configuración que el renderizador utilizará para representar la escena. La posición de un nodo en el árbol del framegraph determina cuándo y dónde el subárbol enraizado en ese nodo será la configuración activa en el proceso de renderizado. Como veremos más adelante, el renderizador recorre este árbol para construir el estado necesario para su algoritmo de renderizado en cada punto del fotograma.
Obviamente, si sólo quieres renderizar un simple cubo en pantalla puedes pensar que esto es una exageración. Sin embargo, tan pronto como quieras empezar a hacer escenas un poco más complejas esto te resultará muy útil. Para los casos más comunes, Qt 3D proporciona algunos framegraphs de ejemplo que están listos para usar.
Demostraremos la flexibilidad del concepto de framegraph presentando algunos ejemplos y los framegraphs resultantes.
Tenga en cuenta que, a diferencia del Scenegraph, que se compone de Entidades y Componentes, el framegraph sólo se compone de nodos anidados que son todos subclases de Qt3DRender::QFrameGraphNode. Esto se debe a que los nodos del framegraph no son objetos simulados en nuestro mundo virtual, sino más bien información de apoyo.
Pronto veremos cómo construir nuestro primer framegraph simple, pero antes presentaremos los nodos framegraph disponibles. Al igual que con el árbol del Scenegraph, las APIs de QML y C++ coinciden 1 a 1, así que puedes elegir la que más te guste. En aras de la legibilidad y la concisión, la API QML fue elegida para este artículo.
La belleza del framegraph es que combinando estos simples tipos de nodos, es posible configurar el renderizador para que se adapte a tus necesidades específicas sin tocar ningún código de renderizado C/C++ de bajo nivel.
Reglas del FrameGraph
Para construir un árbol de framegraph que funcione correctamente, debe conocer algunas reglas sobre cómo se recorre y cómo alimentar al renderizador Qt 3D.
Configuración del Framegraph
El árbol FrameGraph debe ser asignado a la propiedad activeFrameGraph de un componente QRenderSettings, siendo él mismo un componente de la entidad raíz en la escena Qt 3D. Esto es lo que lo convierte en el framegraph activo para el renderizador. Por supuesto, como se trata de una propiedad QML, el framegraph activo (o partes de él) puede ser cambiado sobre la marcha en tiempo de ejecución. Por ejemplo, si desea utilizar diferentes enfoques de renderizado para escenas interiores y exteriores o para activar o desactivar algún efecto especial.
Entity {
id: sceneRoot
components: RenderSettings {
activeFrameGraph: ... // FrameGraph tree
}
}Nota: activeFrameGraph es la propiedad por defecto del componente FrameGraph en QML.
Entity {
id: sceneRoot
components: RenderSettings {
... // FrameGraph tree
}
}Cómo se utiliza el Framegraph
- El renderizador de Qt 3D realiza un recorrido en profundidad del árbol del framegraph. Tenga en cuenta que, debido a que el recorrido es primero en profundidad, el orden en el que se definen los nodos es importante.
- Cuando el renderizador alcanza un nodo hoja del framegraph, recoge todo el estado especificado por la ruta desde el nodo hoja hasta el nodo raíz. Esto define el estado utilizado para renderizar una sección del marco. Si estás interesado en las interioridades de Qt 3D, esta colección de estados se denomina RenderView.
- Dada la configuración contenida en un RenderView, el renderizador reúne todas las entidades del Scenegraph a renderizar, y a partir de ellas construye un conjunto de RenderCommands y los asocia con el RenderView.
- La combinación de RenderView y conjunto de RenderCommands se pasa para su envío a OpenGL.
- Cuando esto se repite para cada nodo hoja en el framegraph, el marco está completo y el renderizador llama a QOpenGLContext::swapBuffers() para mostrar el marco.
En esencia, el framegraph es un método basado en datos para configurar el renderizador Qt 3D. Debido a su naturaleza basada en datos, podemos cambiar la configuración en tiempo de ejecución, permitir a los desarrolladores o diseñadores no-C++ cambiar la estructura de un marco, y probar nuevos enfoques de renderizado sin tener que escribir miles de líneas de código.
Ejemplos de Framegraph
Ahora que conoces las reglas a seguir cuando escribes un árbol framegraph, vamos a ver algunos ejemplos y desglosarlos.
Un Renderizador Avanzado Simple
Renderizado directo es cuando usas OpenGL en su forma tradicional y renderizas directamente al backbuffer un objeto a la vez sombreando cada uno a medida que avanzamos. Esto es opuesto al renderizado diferido donde renderizamos a un G-buffer intermedio. Aquí tenemos un simple FrameGraph que puede ser usado para renderizado en diferido:
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
property alias camera: cameraSelector.camera
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
CameraSelector {
id: cameraSelector
}
}
}Como puedes ver, este árbol tiene una sola hoja y está compuesto por 3 nodos en total como se muestra en el siguiente diagrama.

Usando las reglas definidas anteriormente, este árbol framegraph produce un único RenderView con la siguiente configuración:
- Nodo Hoja -> RenderView
- Viewport que llena toda la pantalla (utiliza coordenadas normalizadas para facilitar el soporte de viewports anidados)
- Los buffers de Color y Profundidad están configurados para ser borrados
- Cámara especificada en la propiedad de cámara expuesta
Varios árboles FrameGraph diferentes pueden producir el mismo resultado de renderizado. Mientras el estado recogido desde la hoja a la raíz sea el mismo, el resultado también será el mismo. Es mejor poner el estado que permanece constante más cerca de la raíz del framegraph ya que esto resultará en menos nodos hoja, y por lo tanto, menos RenderViews en general.
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
property alias camera: cameraSelector.camera
CameraSelector {
id: cameraSelector
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
}
}CameraSelector {
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
}
}Un FrameGraph Multi Viewport
Pasemos a un ejemplo ligeramente más complejo que renderiza un Scenegraph desde el punto de vista de 4 cámaras virtuales en los 4 cuadrantes de la ventana. Esta es una configuración común para CAD 3D o herramientas de modelado o podría ser ajustado para ayudar con la representación de un espejo retrovisor en un juego de carreras de coches o una pantalla de la cámara de circuito cerrado de televisión.

Viewport {
id: mainViewport
normalizedRect: Qt.rect(0, 0, 1, 1)
property alias Camera: cameraSelectorTopLeftViewport.camera
property alias Camera: cameraSelectorTopRightViewport.camera
property alias Camera: cameraSelectorBottomLeftViewport.camera
property alias Camera: cameraSelectorBottomRightViewport.camera
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
Viewport {
id: topLeftViewport
normalizedRect: Qt.rect(0, 0, 0.5, 0.5)
CameraSelector { id: cameraSelectorTopLeftViewport }
}
Viewport {
id: topRightViewport
normalizedRect: Qt.rect(0.5, 0, 0.5, 0.5)
CameraSelector { id: cameraSelectorTopRightViewport }
}
Viewport {
id: bottomLeftViewport
normalizedRect: Qt.rect(0, 0.5, 0.5, 0.5)
CameraSelector { id: cameraSelectorBottomLeftViewport }
}
Viewport {
id: bottomRightViewport
normalizedRect: Qt.rect(0.5, 0.5, 0.5, 0.5)
CameraSelector { id: cameraSelectorBottomRightViewport }
}
}Este árbol es un poco más complejo, con 5 hojas. Siguiendo las mismas reglas que antes construimos 5 objetos RenderView a partir del FrameGraph. Los siguientes diagramas muestran la construcción de los dos primeros RenderViews. Los RenderViews restantes son muy similares al segundo diagrama sólo que con los otros sub-árboles.


En total, los RenderViews creados son:
- RenderView (1)
- Pantalla completa definida
- Los buffers de Color y Profundidad están definidos para ser borrados
- RenderView (2)
- Pantalla completa definida
- Subvista definida (la vista de renderizado se escalará con respecto a su padre)
- CameraSelector especificado
- Vista de renderizado (3)
- Pantalla completa definida
- Subvista definida (la vista de renderizado se escalará con respecto a su padre)
- CameraSelector especificado
- Vista de renderizado (4)
- Pantalla completa definida
- Subvista definida (la vista de renderizado se escalará con respecto a su padre)
- CameraSelector especificado
- Vista de renderizado (5)
- Pantalla completa definida
- Subvista definida (la vista de renderizado se escalará con respecto a su padre)
- CameraSelector especificado
Sin embargo, en este caso el orden es importante. Si el nodo ClearBuffers fuera el último en lugar del primero, se produciría una pantalla negra por la sencilla razón de que todo se borraría justo después de haber sido renderizado tan cuidadosamente. Por una razón similar, no podría ser utilizado como la raíz del FrameGraph ya que eso daría lugar a una llamada para limpiar toda la pantalla para cada uno de nuestros viewports.
Aunque el orden de declaración del FrameGraph es importante, Qt 3D es capaz de procesar cada RenderView en paralelo ya que cada RenderView es independiente de los demás con el fin de generar un conjunto de RenderCommands para ser enviados mientras el estado del RenderView está en efecto.
Qt 3D utiliza un enfoque basado en tareas para el paralelismo que naturalmente escala con el número de núcleos disponibles. Esto se muestra en el siguiente diagrama para el ejemplo anterior.

Los RenderCommands para los RenderViews pueden ser generados en paralelo a través de muchos núcleos, y siempre que tengamos cuidado de enviar los RenderViews en el orden correcto en el hilo de envío OpenGL dedicado, la escena resultante será renderizada correctamente.
Renderizador diferido
Cuando se trata de renderizar, el renderizado diferido es una bestia diferente en términos de configuración del renderizador comparado con el renderizado directo. En lugar de dibujar cada malla y aplicar un efecto shader para sombrearla, el renderizado diferido adopta un método de dos pasadas de renderizado.
En primer lugar, todas las mallas de la escena se dibujan utilizando el mismo sombreador que generará, normalmente para cada fragmento, al menos cuatro valores:
- Vector normal del mundo
- Color (u otras propiedades del material)
- Profundidad
- Vector de posición global
Cada uno de estos valores se almacenará en una textura. Las texturas de normal, color, profundidad y posición forman lo que se denomina G-Buffer. Nada se dibuja en pantalla durante la primera pasada, sino que se dibuja en el G-Buffer listo para su uso posterior.
Una vez que todas las mallas han sido dibujadas, el G-Buffer se llena con todas las mallas que actualmente pueden ser vistas por la cámara. La segunda pasada de render se utiliza entonces para renderizar la escena en el back buffer con el sombreado de color final mediante la lectura de los valores de normal, color y posición de las texturas del G-buffer y la salida de un color en un quad a pantalla completa.
La ventaja de esta técnica es que la gran potencia de cálculo necesaria para los efectos complejos sólo se utiliza durante la segunda pasada en los elementos que realmente están siendo vistos por la cámara. La primera pasada no cuesta mucha potencia de procesamiento, ya que cada malla se dibuja con un simple sombreador. El renderizado diferido, por tanto, desvincula el sombreado y la iluminación del número de objetos de una escena y, en su lugar, lo acopla a la resolución de la pantalla (y del G-Buffer). Se trata de una técnica que se ha utilizado en muchos juegos debido a la posibilidad de utilizar un gran número de luces dinámicas a expensas de un uso adicional de memoria de la GPU.
Viewport { id: root normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0) property GBuffer gBuffer property alias camera: sceneCameraSelector.camera property alias sceneLayer: sceneLayerFilter.layers property alias screenQuadLayer: screenQuadLayerFilter.layers RenderSurfaceSelector { CameraSelector { id: sceneCameraSelector // Fill G-Buffer LayerFilter { id: sceneLayerFilter RenderTargetSelector { id: gBufferTargetSelector target: gBuffer ClearBuffers { buffers: ClearBuffers.ColorDepthBuffer RenderPassFilter { id: geometryPass matchAny: FilterKey { name: "pass" value: "geometry" } } } } } TechniqueFilter { parameters: [ Parameter { name: "color"; value: gBuffer.color }, Parameter { name: "position"; value: gBuffer.position }, Parameter { name: "normal"; value: gBuffer.normal }, Parameter { name: "depth"; value: gBuffer.depth } ] RenderStateSet { // Render FullScreen Quad renderStates: [ BlendEquation { blendFunction: BlendEquation.Add }, BlendEquationArguments { sourceRgb: BlendEquationArguments.SourceAlpha destinationRgb: BlendEquationArguments.DestinationColor } ] LayerFilter { id: screenQuadLayerFilter ClearBuffers { buffers: ClearBuffers.ColorDepthBuffer RenderPassFilter { matchAny: FilterKey { name: "pass" value: "final" } parameters: Parameter { name: "winSize" value: Qt.size(1024, 768) } } } } } } } } }
(El código anterior es una adaptación de qt3d/tests/manual/deferred-renderer-qml).
Gráficamente, el framegraph resultante tiene este aspecto:

Y las RenderViews resultantes son:
- RenderView (1)
- Especificar la cámara a utilizar
- Definir un viewport que llene toda la pantalla
- Seleccionar todas las Entidades para el componente de capa sceneLayer
- Establecer
gBuffercomo el objetivo de render activo - Borre el color y la profundidad en el objetivo de renderizado actual (el
gBuffer) - Seleccione sólo las entidades de la escena que tengan un material y una técnica que coincidan con las anotaciones de la capa sceneLayer. RenderPassFilter
- Vista de Render (2)
- Defina una ventana que llene toda la pantalla
- Seleccione todas las entidades para el componente de capa screenQuadLayer
- Borra los buffers de color y profundidad en el framebuffer actual (la pantalla)
- Seleccione sólo las entidades de la escena que tengan un material y una técnica que coincidan con las anotaciones de la capa RenderPassFilter
Otras ventajas del Framegraph
Dado que el árbol FrameGraph está totalmente basado en datos y puede ser modificado dinámicamente en tiempo de ejecución, puedes:
- Tener diferentes árboles de framegraph para diferentes plataformas y hardware y seleccionar el más apropiado en tiempo de ejecución
- Añadir y habilitar fácilmente la depuración visual en una escena
- Utilizar diferentes árboles FrameGraph dependiendo de la naturaleza de lo que necesites renderizar para una región concreta de la escena
- Implemente una nueva técnica de renderizado sin tener que modificar los componentes internos de Qt 3D.
Conclusión
Hemos introducido el FrameGraph y los tipos de nodos que lo componen. Luego pasamos a discutir algunos ejemplos para ilustrar las reglas de construcción del framegraph y cómo el motor Qt 3D utiliza el framegraph entre bastidores. A estas alturas deberías tener una buena visión general del FrameGraph y de cómo puede ser usado (quizás para añadir un pase de z-fill temprano a un renderizador forward). También debe tener siempre en cuenta que el FrameGraph es una herramienta para su uso de modo que usted no está atado a la proporcionada renderizador y materiales que Qt 3D proporciona fuera de la caja.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.