This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Tous les objets affichés à l’écran par le moteur, qu’il s’agisse de sprites, de modèles, de tuiles, de particules ou de nœuds GUI, sont dessinés par un système de rendu. Au cœur de ce système se trouve un script de rendu qui contrôle le pipeline de rendu. Par défaut, chaque objet 2D est dessiné avec la bonne image bitmap, le mélange spécifié et la bonne profondeur Z : vous n’aurez donc peut-être jamais à vous préoccuper du rendu au-delà de l’ordre de dessin et d’un mélange simple. Le pipeline par défaut convient à la plupart des jeux 2D, mais votre jeu peut avoir des besoins particuliers. Dans ce cas, Defold vous permet d’écrire un pipeline de rendu sur mesure.
Le pipeline de rendu contrôle les éléments à dessiner, le moment où les dessiner et l’endroit où les dessiner. Les éléments à dessiner sont contrôlés par les prédicats de rendu. Le moment où dessiner un prédicat est contrôlé dans le script de rendu, et l’endroit où le dessiner est contrôlé par la projection de vue. Le pipeline de rendu peut également éliminer les éléments graphiques dessinés par un prédicat de rendu qui se trouvent en dehors d’une boîte englobante ou d’un volume de visibilité définis. Ce processus s’appelle l’élimination hors du volume de visibilité (frustum culling).
Le fichier de rendu contient une référence au script de rendu actuel ainsi que les matériaux personnalisés qui doivent être accessibles dans le script de rendu (à utiliser avec render.enable_material())
Au cœur du pipeline de rendu se trouve le script de rendu. Il s’agit d’un script Lua contenant les fonctions init(), update() et on_message(), principalement utilisé pour interagir avec l’API graphique sous-jacente. Le script de rendu occupe une place particulière dans le cycle de vie de votre jeu. Vous trouverez des précisions dans la documentation sur le cycle de vie de l’application.
Dans le dossier “Builtins” de vos projets, vous trouverez la ressource de rendu par défaut (“default.render”) et le script de rendu par défaut (“default.render_script”).

Pour configurer un système de rendu personnalisé :
Copiez les fichiers “default.render” et “default.render_script” à un emplacement dans la hiérarchie de votre projet. Vous pouvez bien sûr créer un script de rendu à partir de zéro, mais il est préférable de commencer par une copie du script par défaut, surtout si vous débutez avec Defold et/ou la programmation graphique.
Modifiez votre copie du fichier “default.render” et changez la propriété Script pour qu’elle fasse référence à votre copie du script de rendu.
Changez la propriété Render (sous bootstrap) dans le fichier de paramètres game.project pour qu’elle fasse référence à votre copie du fichier “default.render”.
Pour contrôler l’ordre de dessin des objets, vous créez des prédicats de rendu. Un prédicat déclare les éléments à dessiner à partir d’une sélection d’étiquettes de matériau.
Chaque objet dessiné à l’écran possède un matériau qui contrôle la manière dont il doit être dessiné. Dans le matériau, vous spécifiez une ou plusieurs étiquettes à associer au matériau.
Dans votre script de rendu, vous pouvez ensuite créer un prédicat de rendu et spécifier les étiquettes qui doivent lui appartenir. Lorsque vous demandez au moteur de dessiner le prédicat, chaque objet dont le matériau contient toutes les étiquettes spécifiées pour ce prédicat est dessiné.
Sprite 1 Sprite 2 Sprite 3 Sprite 4
Material A Material A Material B Material C
outlined outlined greyscale outlined
tree tree tree house
-- a predicate matching all sprites with tag "tree"
local trees = render.predicate({"tree"})
-- will draw Sprite 1, 2 and 3
render.draw(trees)
-- a predicate matching all sprites with tag "outlined"
local outlined = render.predicate({"outlined"})
-- will draw Sprite 1, 2 and 4
render.draw(outlined)
-- a predicate matching all sprites with tags "outlined" AND "tree"
local outlined_trees = render.predicate({"outlined", "tree"})
-- will draw Sprite 1 and 2
render.draw(outlined_trees)
Vous trouverez une description détaillée du fonctionnement des matériaux dans la documentation sur les matériaux.
Le script de rendu par défaut est configuré pour utiliser une projection orthographique adaptée aux jeux 2D. Il propose trois projections orthographiques différentes : Stretch (par défaut), Fixed Fit et Fixed. Au lieu des projections orthographiques du script de rendu par défaut, vous pouvez également utiliser la matrice de projection fournie par un composant (component) caméra.
La projection étirée dessine toujours une zone de votre jeu correspondant aux dimensions définies dans game.project, même lorsque la fenêtre est redimensionnée. Si le rapport largeur/hauteur change, le contenu du jeu est étiré verticalement ou horizontalement :

Projection étirée avec la taille de fenêtre d’origine

Projection étirée avec la fenêtre étirée horizontalement
La projection étirée est la projection par défaut, mais si vous en avez choisi une autre et souhaitez y revenir, envoyez un message au script de rendu :
msg.post("@render:", "use_stretch_projection", { near = -1, far = 1 })
Comme la projection étirée, la projection à ajustement fixe affiche toujours une zone du jeu correspondant aux dimensions définies dans game.project. Toutefois, si la fenêtre est redimensionnée et que le rapport largeur/hauteur change, le contenu du jeu conserve son rapport largeur/hauteur d’origine et une plus grande partie du jeu s’affiche verticalement ou horizontalement :

Projection à ajustement fixe avec la taille de fenêtre d’origine

Projection à ajustement fixe avec la fenêtre étirée horizontalement

Projection à ajustement fixe avec la fenêtre réduite à 50 % de sa taille d’origine
Pour activer la projection à ajustement fixe, envoyez un message au script de rendu :
msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
La projection fixe conserve le rapport largeur/hauteur d’origine et dessine le contenu de votre jeu avec un niveau de zoom fixe. Cela signifie que, si le niveau de zoom est différent de 100 %, elle affiche une zone du jeu plus grande ou plus petite que celle définie par les dimensions dans game.project :

Projection fixe avec un zoom réglé sur 2

Projection fixe avec un zoom réglé sur 0.5

Projection fixe avec un zoom réglé sur 2 et la fenêtre réduite à 50 % de sa taille d’origine
Pour activer la projection fixe, envoyez un message au script de rendu :
msg.post("@render:", "use_fixed_projection", { near = -1, far = 1, zoom = 2 })
Lorsque vous utilisez le script de rendu par défaut et que des composants caméra sont activés dans le projet, ils ont priorité sur toute autre vue ou projection définie dans le script de rendu. Pour en savoir plus sur l’utilisation des composants caméra dans les scripts de rendu, consultez la documentation sur les caméras.
Les caméras orthographiques prennent en charge un paramètre Orthographic Mode qui contrôle la façon dont la caméra s’adapte à la fenêtre :
Fixed utilise la valeur Orthographic Zoom de la caméra.Auto Fit (contenir) garde toute la zone de conception visible.Auto Cover (couvrir) remplit la fenêtre et peut recadrer le contenu.Vous pouvez changer de mode dans l’éditeur ou à l’exécution via l’API Camera :
-- Use auto-fit behavior with an orthographic camera
camera.set_orthographic_mode("main:/go#camera", camera.ORTHO_MODE_AUTO_FIT)
-- Query current mode
local mode = camera.get_orthographic_mode("main:/go#camera")
L’API de rendu de Defold permet aux développeurs d’effectuer ce que l’on appelle l’élimination hors du volume de visibilité. Lorsque cette élimination est activée, tout élément graphique situé en dehors d’une boîte englobante ou d’un volume de visibilité définis est ignoré. Dans un grand monde de jeu (game world) dont seule une partie est visible à la fois, l’élimination hors du volume de visibilité peut réduire considérablement la quantité de données à envoyer au GPU pour le rendu, ce qui améliore les performances et économise la batterie (sur les appareils mobiles). Il est courant d’utiliser la vue et la projection de la caméra pour créer la boîte englobante. Le script de rendu par défaut utilise la vue et la projection (de la caméra) pour calculer un volume de visibilité.
Activez l’élimination hors du volume de visibilité pour un appel de dessin en passant une matrice de vue-projection dans l’option frustum de render.draw() :
local frustum = self.proj * self.view
render.draw(predicates.particle, { frustum = frustum })
Lors du rendu avec un composant caméra, render.set_camera() peut utiliser automatiquement la matrice de vue-projection de la caméra pour les appels de dessin suivants :
render.set_camera("main:/go#camera", { use_frustum = true })
render.draw(predicates.particle)
render.set_camera()
Lorsque l’une ou l’autre de ces méthodes est utilisée, les émetteurs Particle FX sont éliminés en fonction de leurs limites.
L’élimination hors du volume de visibilité est implémentée dans le moteur par type de composant. État actuel :
| Composant | Pris en charge |
|---|---|
| Sprite | OUI |
| Model | OUI |
| Mesh | OUI (1) |
| Label | OUI |
| Spine | OUI |
| Particle fx | OUI |
| Tilemap | OUI |
| Rive | NON |
1 = La boîte englobante du composant Mesh doit être définie par le développeur. En savoir plus.
À partir de Defold 1.13.0, les sommets des primitives des composants sont ordonnés dans le sens antihoraire, avec la normale de la primitive dirigée vers la caméra. Les sprites, les nœuds GUI, les tilemaps (grilles de tuiles) et les Particle FX utilisent le même ordre que les autres types de composants ; les mêmes paramètres d’élimination des faces peuvent donc être utilisés pour tous les composants.
Cela peut affecter les projets qui configurent l’élimination des faces pour des composants autres que les modèles. Si un composant est éliminé de manière inattendue, assurez-vous que les faces arrière sont sélectionnées avec render.set_cull_face(graphics.FACE_TYPE_BACK), ou supprimez l’appel à render.set_cull_face() pour utiliser le mode par défaut graphics.FACE_TYPE_BACK.
Lorsque des composants sont dessinés, on précise généralement le système de coordonnées dans lequel ils le sont. Dans la plupart des jeux, certains composants sont dessinés dans l’espace du monde et d’autres dans l’espace de l’écran.
Les composants GUI et leurs nœuds sont généralement dessinés dans le système de coordonnées de l’espace de l’écran, où le coin inférieur gauche de l’écran a pour coordonnées (0,0) et le coin supérieur droit (largeur de l’écran, hauteur de l’écran). Le système de coordonnées de l’espace de l’écran n’est jamais décalé ni translaté d’une autre manière par une caméra. Les nœuds GUI restent ainsi toujours dessinés à l’écran, quel que soit le rendu du monde.
Les sprites, les tilemaps et les autres composants utilisés par les objets de jeu (game objects) de votre monde de jeu sont généralement dessinés dans le système de coordonnées de l’espace du monde. Si vous ne modifiez pas votre script de rendu et n’utilisez aucun composant caméra pour changer la projection de vue, ce système de coordonnées est identique à celui de l’espace de l’écran. Dès que vous ajoutez une caméra et la déplacez ou changez la projection de vue, les deux systèmes de coordonnées divergent. Lorsque la caméra se déplace, le coin inférieur gauche de l’écran est décalé par rapport à (0, 0), de sorte que d’autres parties du monde sont dessinées. Si la projection change, les coordonnées sont à la fois translatées (c’est-à-dire décalées par rapport à 0, 0) et modifiées par un facteur d’échelle.
Voici le code d’un script de rendu personnalisé qui est une version légèrement modifiée du script intégré.
init() sert à configurer les prédicats, la vue et la couleur d’effacement. Ces variables seront utilisées lors du rendu proprement dit.function init(self)
-- Define the render predicates. Each predicate is drawn by itself and
-- that allows us to change the state of OpenGL between the draws.
self.predicates = create_predicates("tile", "gui", "text", "particle", "model")
-- Create and fill data tables will be used in update()
local state = create_state()
self.state = state
local camera_world = create_camera(state, "camera_world", true)
init_camera(camera_world, get_stretch_projection)
local camera_gui = create_camera(state, "camera_gui")
init_camera(camera_gui, get_gui_projection)
update_state(state)
end
update() est appelée une fois par image. Elle effectue le dessin proprement dit en appelant les API OpenGL ES sous-jacentes (OpenGL Embedded Systems API). Pour bien comprendre ce qui se passe dans la fonction update(), vous devez comprendre le fonctionnement d’OpenGL. Il existe de nombreuses ressources de qualité sur OpenGL ES. Le site officiel est un bon point de départ. Vous le trouverez à l’adresse https://www.khronos.org/opengles/
Cet exemple contient la configuration nécessaire pour dessiner des modèles 3D. La fonction init() a défini un prédicat self.predicates.model. Ailleurs, un matériau portant l’étiquette “model” a été créé. Certains composants de modèle utilisent également ce matériau :
function update(self)
local state = self.state
if not state.valid then
if not update_state(state) then
return
end
end
local predicates = self.predicates
-- clear screen buffers
--
render.set_depth_mask(true)
render.set_stencil_mask(0xff)
render.clear(state.clear_buffers)
local camera_world = state.cameras.camera_world
render.set_viewport(0, 0, state.window_width, state.window_height)
render.set_view(camera_world.view)
render.set_projection(camera_world.proj)
-- render models
--
render.set_blend_func(graphics.BLEND_FACTOR_SRC_ALPHA, graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)
render.enable_state(graphics.STATE_CULL_FACE)
render.enable_state(graphics.STATE_DEPTH_TEST)
render.set_depth_mask(true)
render.draw(predicates.model_pred)
render.set_depth_mask(false)
render.disable_state(graphics.STATE_DEPTH_TEST)
render.disable_state(graphics.STATE_CULL_FACE)
-- render world (sprites, tilemaps, particles etc)
--
render.set_blend_func(graphics.BLEND_FACTOR_SRC_ALPHA, graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)
render.enable_state(graphics.STATE_DEPTH_TEST)
render.enable_state(graphics.STATE_STENCIL_TEST)
render.enable_state(graphics.STATE_BLEND)
render.draw(predicates.tile)
render.draw(predicates.particle)
render.disable_state(graphics.STATE_STENCIL_TEST)
render.disable_state(graphics.STATE_DEPTH_TEST)
-- debug
render.draw_debug3d()
-- render GUI
--
local camera_gui = state.cameras.camera_gui
render.set_view(camera_gui.view)
render.set_projection(camera_gui.proj)
render.enable_state(graphics.STATE_STENCIL_TEST)
render.draw(predicates.gui, camera_gui.frustum)
render.draw(predicates.text, camera_gui.frustum)
render.disable_state(graphics.STATE_STENCIL_TEST)
end
Jusqu’ici, ce script de rendu est simple et direct. Il dessine de la même manière à chaque image. Cependant, il est parfois souhaitable de pouvoir introduire un état dans le script de rendu et d’effectuer différentes opérations en fonction de cet état. Il peut aussi être souhaitable de communiquer avec le script de rendu depuis d’autres parties du code du jeu.
on_message() et recevoir des messages d’autres parties de votre jeu ou de votre application. La caméra est un exemple courant de composant externe qui envoie des informations au script de rendu. Un composant caméra qui a obtenu le focus de la caméra envoie automatiquement sa vue et sa projection au script de rendu à chaque image. Ce message s’appelle "set_view_projection" :local MSG_CLEAR_COLOR = hash("clear_color")
local MSG_WINDOW_RESIZED = hash("window_resized")
local MSG_SET_VIEW_PROJ = hash("set_view_projection")
function on_message(self, message_id, message)
if message_id == MSG_CLEAR_COLOR then
-- Someone sent us a new clear color to be used.
update_clear_color(state, message.color)
elseif message_id == MSG_SET_VIEW_PROJ then
-- The camera component that has camera focus will sent set_view_projection
-- messages to the @render socket. We can use the camera information to
-- set view (and possibly projection) of the rendering.
camera.view = message.view
self.camera_projection = message.projection or vmath.matrix4()
update_camera(camera, state)
end
end
Cependant, n’importe quel script ou script GUI peut envoyer des messages au script de rendu via le socket spécial @render :
-- Change the clear color.
msg.post("@render:", "clear_color", { color = vmath.vector4(0.3, 0.4, 0.5, 0) })
Pour transmettre certaines ressources du moteur au script de rendu, vous pouvez les ajouter au tableau Render Resources du fichier .render affecté au projet :

Utilisation de ces ressources dans un script de rendu :
-- "my_material" will now be used for all draw calls associated with the predicate
render.enable_material("my_material")
-- anything drawn by the predicate will end up in "my_render_target"
render.set_render_target("my_render_target")
render.draw(self.my_full_screen_predicate)
render.set_render_target(render.RENDER_TARGET_DEFAULT)
render.disable_material()
-- bind the render target result texture to whatever is getting rendered via the predicate
render.enable_texture(0, "my_render_target", graphics.BUFFER_TYPE_COLOR0_BIT)
render.draw(self.my_tile_predicate)
Defold ne prend actuellement en charge que Materials et Render Targets comme ressources de rendu référencées, mais ce système prendra en charge davantage de types de ressources à l’avenir.
Dans Defold, les textures sont représentées en interne par un identifiant opaque (handle), qui correspond essentiellement à un nombre devant identifier de manière unique un objet texture partout dans le moteur. Vous pouvez ainsi relier le monde des objets de jeu à celui du rendu en transmettant ces identifiants entre le système de rendu et un script d’objet de jeu. Par exemple, un script attaché à un objet de jeu peut créer une texture dynamique et l’envoyer au système de rendu pour qu’elle soit utilisée comme texture globale dans une commande de dessin.
Dans un fichier .script :
local my_texture_resource = resource.create_texture("/my_texture.texture", tparams)
-- note: my_texture_resource is a hash to the resource path, which can't be used as a handle!
local my_texture_handle = resource.get_texture_info(my_texture_resource)
-- my_texture_handle contains information about the texture, such as width, height and so on
-- it does also contain the handle, which is what we are after
msg.post("@render:", "set_texture", { handle = my_texture_handle.handle })
Dans un fichier .render_script :
function on_message(self, message_id, message)
if message_id == hash("set_texture") then
self.my_texture = message.handle
end
end
function update(self)
-- bind the custom texture to the draw state
render.enable_texture(0, self.my_texture)
-- do drawing..
end
Il n’existe actuellement aucun moyen de changer la texture vers laquelle une ressource doit pointer ; vous pouvez uniquement utiliser des identifiants bruts de cette manière dans le script de rendu.
L’API des scripts de rendu de Defold traduit les opérations de rendu vers les API graphiques suivantes :
| Système | API graphique | Note |
|---|---|---|
| macOS | OpenGL 3.3 ou Metal | Vulkan via MoltenVK |
| Windows | OpenGL 3.3 ou Vulkan 1.1 | |
| Linux x86-64 | OpenGL 3.3 ou Vulkan 1.1 | |
| Linux ARM64 | OpenGL ES ou Vulkan 1.1 | EGL/GLES est utilisé par défaut |
| Android | OpenGLES 3.0 ou Vulkan 1.1 | Repli sur OpenGLES 2.0 |
| iOS | OpenGLES 3.0 ou Metal | Vulkan via MoltenVK |
| HTML5 | WebGL 2.0 ou WebGPU | Repli sur WebGL 1.0 |
"set_view_projection""window_resized"local MSG_WINDOW_RESIZED = hash("window_resized")
function on_message(self, message_id, message)
if message_id == MSG_WINDOW_RESIZED then
-- The window was resized. message.width and message.height contain the new dimensions.
...
end
end
"draw_line"ray_casts, des vecteurs et d’autres éléments. Les lignes sont dessinées par l’appel à render.draw_debug3d().-- draw a white line
local p1 = vmath.vector3(0, 0, 0)
local p2 = vmath.vector3(1000, 1000, 0)
local col = vmath.vector4(1, 1, 1, 1)
msg.post("@render:", "draw_line", { start_point = p1, end_point = p2, color = col } )
"draw_text"always_on_top.font. La police système possède un matériau portant l’étiquette debug_text et est dessinée avec les autres textes dans le script de rendu par défaut.-- draw a text message
local pos = vmath.vector3(500, 500, 0)
msg.post("@render:", "draw_text", { text = "Hello world!", position = pos })
Le profileur visuel, accessible via le message "toggle_profile" envoyé au socket @system, ne fait pas partie du système de rendu programmable. Il est dessiné séparément de votre script de rendu.
Un appel de dessin désigne le processus de configuration du GPU pour dessiner un objet à l’écran à l’aide d’une texture et d’un matériau, avec éventuellement des paramètres supplémentaires. Ce processus consomme généralement beaucoup de ressources, et il est recommandé de réduire au minimum le nombre d’appels de dessin. Vous pouvez mesurer le nombre d’appels de dessin et le temps nécessaire à leur rendu à l’aide du profileur intégré.
Defold tente de regrouper les opérations de rendu par lots pour réduire le nombre d’appels de dessin selon un ensemble de règles définies ci-dessous. Ces règles diffèrent entre les composants GUI et tous les autres types de composants.
Chaque appel à render.draw() contrôle le tri des entrées correspondantes ordonnées dans l’espace du monde. L’ordre par défaut est render.SORT_BACK_TO_FRONT ; utilisez render.SORT_FRONT_TO_BACK pour dessiner du plus proche au plus éloigné, ou render.SORT_NONE pour conserver l’ordre d’insertion :
render.draw(self.opaque_predicate, {
sort_order = render.SORT_FRONT_TO_BACK
})
render.draw(self.transparent_predicate, {
sort_order = render.SORT_BACK_TO_FRONT
})
L’ordre choisi détermine les entrées adjacentes et peut donc affecter le regroupement par lots. Dans cette liste ordonnée, chaque objet est regroupé dans le même appel de dessin que l’objet précédent si les conditions suivantes sont réunies :
Cela signifie que, si deux composants sprite du même proxy de collection sont adjacents après le tri choisi et utilisent la même texture, le même matériau et les mêmes constantes, ils sont regroupés dans le même appel de dessin.
Le rendu des nœuds d’un composant GUI se fait du haut vers le bas de la liste des nœuds. Chaque nœud de la liste est regroupé dans le même appel de dessin que le nœud précédent si les conditions suivantes sont réunies :
Le rendu des nœuds se fait par composant. Cela signifie que les nœuds de différents composants GUI ne sont pas regroupés par lots.
La possibilité d’organiser les nœuds en hiérarchies permet de les regrouper facilement en unités gérables. Toutefois, les hiérarchies peuvent empêcher le rendu par lots si vous mélangez différents types de nœuds. Les couches GUI permettent de regrouper plus efficacement les nœuds GUI par lots tout en conservant leur hiérarchie. Pour en savoir plus sur les couches GUI et leur effet sur les appels de dessin, consultez le manuel de l’interface graphique.