IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 Java Discussion :

Comment utiliser un BufferStrategy ?


Sujet :

Java

  1. #1
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut Comment utiliser un BufferStrategy ?
    Bonjour tout le monde,
    Est t'il possible de dessiner avec un BufferStrategy pixel par pixel?
    J'ai ce code pour un BufferedImage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    BufferedImage image = new BufferedImage(window.getWidth(), window.getHeight(), BufferedImage.TYPE_INT_RGB);
    int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    Est ce qu'il existe ce principe la pour BufferStrategy ou bien est ce que je doit dessiner le BufferedImage dans le BufferStrategy?
    Si je fait ça, je perd peut être l’intérêt du BufferStrategy.
    Et quelqu'un peut t'il m'expliquer quel est la différence entre buffering hardware et software?
    BufferedImage = software? BufferStrategy = hardware?
    Quel est la différence mécanique entre les deux?
    Merci de votre aide.
    Des jours c'est facile, des jours c'est pas facile, mais c'est jamais le même jour.

  2. #2
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    BufferStrategy and BufferCapabilities sur le Java Tutorial d'Oracle

    A priori non dans le sens ou tu ne peux manipuler qu'un Graphics (ou alors tu fais des drawRect() de taille 1x1 ce qui n'est pas forcement efficace). Par contre tu peux dessiner pixel par pixel (setRGB()) dans le raster d'une image que tu dessines ensuite d'un bloc dans le buffer comme tu le pressentait.

    EDIT - ca support acceleration matérielle alors que simplement utiliser un buffer offscreen c'est tout fait de manière logicielle. Ce faisant tu utilises une technique assez ancienne utilisée par les demo-makers d’antan pour faire des changement de contexte/scene/page/buffer rapide (page-flipping) ce qui permettait a l’époque de faire des animations assez chiadées avec peu de puissance et de moyens et surtout sans acceleration 2D sur les petites cartes video poussives qu'on avait il y a 20-30 ans en accédant directement a la mémoire video de la carte.

    Citation Envoyé par https://docs.oracle.com/javase/10/docs/api/java/awt/image/BufferStrategy.html
    The BufferStrategy class represents the mechanism with which to organize complex memory on a particular Canvas or Window. Hardware and software limitations determine whether and how a particular buffer strategy can be implemented. These limitations are detectable through the capabilities of the GraphicsConfiguration used when creating the Canvas or Window.
    It is worth noting that the terms buffer and surface are meant to be synonymous: an area of contiguous memory, either in video device memory or in system memory.

    There are several types of complex buffer strategies, including sequential ring buffering and blit buffering. Sequential ring buffering (i.e., double or triple buffering) is the most common; an application draws to a single back buffer and then moves the contents to the front (display) in a single step, either by copying the data or moving the video pointer. Moving the video pointer exchanges the buffers so that the first buffer drawn becomes the front buffer, or what is currently displayed on the device; this is called page flipping.

    Alternatively, the contents of the back buffer can be copied, or blitted forward in a chain instead of moving the video pointer.


    Double buffering:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
                        ***********         ***********
                        *         * ------> *         *
     [To display] <---- * Front B *   Show  * Back B. * <---- Rendering
                        *         * <------ *         *
                        ***********         ***********
    Triple buffering:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     [To      ***********         ***********        ***********
     display] *         * --------+---------+------> *         *
        <---- * Front B *   Show  * Mid. B. *        * Back B. * <---- Rendering
              *         * <------ *         * <----- *         *
              ***********         ***********        ***********
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  3. #3
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut
    Ok merci pour ta réponse.
    J'aurais dû commencé par le commencement.
    Mon objectif est de faire un outil qui me gère la lumière dans un jeu 2d.
    Actuellement je fait ça avec des «Area.substract()» mais tu peut pas faire de dégradés.
    Tout ça dans une Frame avec le code que j'ai donné plus haut.
    Donc tu me dit que le double buffering hardware c'est dépasser.
    Du coup quels objets je devrais utilisé?
    Des jours c'est facile, des jours c'est pas facile, mais c'est jamais le même jour.

  4. #4
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Non pas du tout ce n'est pas dépassé, ca permet de faire des trucs super rapides et sympa et c'est toujours utilisé quand tu fais de la 2D et de la video de bas niveau (pour la 3D je ne sais pas par contre). Le truc c'est que quand tu passes ton temps a utiliser des API de haut niveau comme moi (Java2D ou JavaFX) et ben tu perds l'habitude de manipuler ce genre de choses. Par contre souvent a cœur de ces API de haut niveau il y a des accelerations qui sont faite avec ce genre de chose.

    Il est tout a fait possible d'utiliser des degrades en manipulant des alpha-composites ! Cela permet également d'utiliser des masques avec des bords adoucis (via un blur par exemple) chose pas possible si on se contente d'utiliser un clip. Dans une composition la couleur de remplissage importe donc si c'est un degrade (d'une couleur a une autre ou d'une couleur solide vers la couleur transparente) alors ca a bien un impact sur le résultat final. Si j'ai le temps aujourd'hui je vais essayer de faire un exemple en Java2D.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  5. #5
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Voici un exemple rapide et pas du tout optimise de ce qu'on peut faire en utilisant les alpha composites. Ici pour simuler un brouillard de guerre et un champ de vision. J'ai tape ca assez vite donc il y a sans doute des trous de performance partout (il suffit d'agrandir la fenêtre pour voir que ca rame bien - ca ira mieux peut-être en n'appliquant pas de blur dans la zone visible ou en changeant les rendering hints des graphics) donc il y a pas mal d'optimisations possibles a faire dont en autre le fait de faire que le calque visible ne soit qu'une petite image au lieu d'une image de la meme taille que la zone de jeu et le classique usage du clip pour restreindre ce qui est redessine (rien ne change jamais sur les bords de la fenêtre sauf en cas de redimensionnement donc ca sert a rien de les redessiner).

    les touches droite et gauche permettent de faire tourner le champ de vision autour du joueur, la touche espace permet d’exporter les principaux calques dans le repertoire de l'application.

    PS : c'est du code JDK 10 qui a été testé sur le JDK 11 preview. Donc il faut remplacer var par le type exact pour faire tourner sur le JDK 9 ou moins.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    package composites;
     
    import javax.imageio.ImageIO;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.geom.Arc2D;
    import java.awt.geom.Ellipse2D;
    import java.awt.image.BufferedImage;
    import java.awt.image.ConvolveOp;
    import java.awt.image.Kernel;
    import java.io.File;
    import java.util.Optional;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public final class Main extends JPanel {
     
        private static final Paint FOG = new Color(0, 0, 10, 150);
        private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
        private static final ConvolveOp BOX_BLUR_5 = createBoxBlurOp(5);
        private static final ConvolveOp BOX_BLUR_20 = createBoxBlurOp(20);
        private final Shape player;
        private final Shape fow;
        private final Paint fowFill;
        private Shape playground;
        private final Paint backgroundFill;
        private BufferedImage defaultBackground;
        private BufferedImage visibleBackground;
        private BufferedImage visibleMask;
        private BufferedImage fogOgWarMask;
        private BufferedImage fogOfWar;
        private float fowAngle = 0;
     
        private Main() {
            player = new Ellipse2D.Float(-5, -5, 10, 10);
            fow = new Arc2D.Float(-200, -200, 400, 400, -45, 90, Arc2D.PIE);
            fowFill = new RadialGradientPaint(0, 0, 100, new float[]{0.5f, 0.95f}, new Color[]{Color.WHITE, TRANSPARENT}, MultipleGradientPaint.CycleMethod.NO_CYCLE);
            backgroundFill = createBackgroundTexture();
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent event) {
                    final var insets = getInsets();
                    final int width = Math.max(0, getWidth() - (insets.left + insets.right));
                    final int height = Math.max(0, getHeight() - (insets.top + insets.bottom));
                    playground = new Rectangle(0, 0, width, height);
                    // Clear to make sure layers are re-created
                    visibleMask = null;
                    fogOgWarMask = null;
                    defaultBackground = null;
                    visibleBackground = null;
                    fogOfWar = null;
                    repaint();
                }
            });
            setFocusable(true);
            requestFocus();
            addKeyListener(new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent event) {
                    switch (event.getKeyCode()) {
                        case KeyEvent.VK_SPACE: {
                            exportLayers();
                        }
                        break;
                        default:
                    }
                    event.consume();
                }
     
                @Override
                public void keyPressed(KeyEvent event) {
                    switch (event.getKeyCode()) {
                        case KeyEvent.VK_LEFT: {
                            fowAngle--;
                        }
                        break;
                        case KeyEvent.VK_RIGHT: {
                            fowAngle++;
                        }
                        break;
                        default:
                    }
                    repaint();
                    event.consume();
                }
            });
        }
     
        public static void main(String... args) {
            SwingUtilities.invokeLater(() -> launch(args));
        }
     
        private static void launch(String... args) {
            final var frame = new JFrame();
            frame.setContentPane(new Main());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(new Dimension(500, 500));
            frame.setVisible(true);
        }
     
        private static Paint createBackgroundTexture() {
            final var image = createAcceleratedImage(100, 100, Transparency.TRANSLUCENT);
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.setPaint(Color.WHITE);
                g2d.fillRect(0, 0, 100, 100);
                g2d.setPaint(Color.BLUE);
                g2d.fillRect(25, 25, 50, 50);
            } finally {
                g2d.dispose();
            }
            return new TexturePaint(image, new Rectangle(100, 100));
        }
     
        private static BufferedImage createAcceleratedImage(final int width, final int height, final int transparency) {
            final var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            final var gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
            return gc.createCompatibleImage(width, height, transparency);
        }
     
        private static BufferedImage toAccceleratedimage(final BufferedImage source) {
            final var destination = createAcceleratedImage(source.getWidth(), source.getHeight(), source.getTransparency());
            final var g2d = setupGraphics(destination.createGraphics());
            try {
                g2d.drawImage(source, 0, 0, null);
            } finally {
                g2d.dispose();
            }
            return destination;
        }
     
        private static Graphics2D setupGraphics(final Graphics2D g2d) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            return g2d;
        }
     
        private static BufferedImage clearImage(final BufferedImage image) {
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.setComposite(AlphaComposite.Clear);
                g2d.setPaint(Color.BLACK);
                g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
            } finally {
                g2d.dispose();
            }
            return image;
        }
     
        private static void exportImage(final BufferedImage image, final String name) {
            try {
                ImageIO.write(image, "png", new File(name));
            } catch (Exception ex) {
                Logger.getGlobal().log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
     
        private static ConvolveOp createBoxBlurOp(final int radius) {
            final int size = radius * 2 + 1;
            final float weight = 1.0f / (size * size);
            final float[] data = new float[size * size];
            for (int i = 0; i < data.length; i++) {
                data[i] = weight;
            }
            final var kernel = new Kernel(size, size, data);
            return new ConvolveOp(kernel);
        }
     
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            final var insets = getInsets();
            final int width = Math.max(0, getWidth() - (insets.left + insets.right));
            final int height = Math.max(0, getHeight() - (insets.top + insets.bottom));
            if (width == 0 || height == 0 || playground == null) {
                return;
            }
            final double centerX = width / 2d;
            final double centerY = height / 2d;
            final var g2d = setupGraphics((Graphics2D) g.create(insets.left, insets.top, width, height));
            try {
                visibleMask = renderVisibleFowMask(visibleMask, width, height);
                fogOgWarMask = renderFogOfWarMask(fogOgWarMask, width, height);
                defaultBackground = renderDefaultBackground(defaultBackground, width, height);
                visibleBackground = renderVisibleBackground(visibleBackground, width, height);
                fogOfWar = renderFogOfWar(fogOfWar, width, height);
                g2d.drawImage(defaultBackground, 0, 0, null);
                g2d.drawImage(visibleBackground, 0, 0, null);
                g2d.translate(centerX, centerY);
                g2d.setPaint(Color.RED);
                g2d.fill(player);
                g2d.translate(-centerX, -centerY);
                g2d.drawImage(fogOfWar, 0, 0, null);
            } finally {
                g2d.dispose();
            }
        }
     
        private BufferedImage renderDefaultBackground(BufferedImage image, final int width, int height) {
            // This background is static, no need to re-render every time.
            if (image == null || image.getWidth() != width || image.getHeight() != height) {
                image = createAcceleratedImage(width, height, Transparency.OPAQUE);
                final var g2d = setupGraphics(image.createGraphics());
                try {
                    g2d.setPaint(backgroundFill);
                    g2d.fill(playground);
                } finally {
                    g2d.dispose();
                }
                image = BOX_BLUR_20.filter(image, null);
                image = toAccceleratedimage(image);
            }
            return image;
        }
     
        private BufferedImage renderVisibleBackground(BufferedImage image, final int width, int height) {
            if (image == null) {
                image = createAcceleratedImage(width, height, Transparency.TRANSLUCENT);
            } else {
                image = clearImage(image);
            }
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.drawImage(visibleMask, 0, 0, null);
                g2d.setComposite(AlphaComposite.SrcIn);
                g2d.setPaint(backgroundFill);
                g2d.fill(playground);
            } finally {
                g2d.dispose();
            }
            return image;
        }
     
        private BufferedImage renderVisibleFowMask(BufferedImage image, final int width, int height) {
            if (image == null) {
                image = createAcceleratedImage(width, height, Transparency.TRANSLUCENT);
            } else {
                image = clearImage(image);
            }
            final double centerX = width / 2d;
            final double centerY = height / 2d;
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.translate(centerX, centerY);
                g2d.rotate(2 * Math.PI * fowAngle / 360d);
                g2d.setPaint(fowFill);
                g2d.fill(fow);
            } finally {
                g2d.dispose();
            }
            image = BOX_BLUR_5.filter(image, null);
            image = toAccceleratedimage(image);
            return image;
        }
     
        private BufferedImage renderFogOfWarMask(BufferedImage image, final int width, int height) {
            if (image == null) {
                image = createAcceleratedImage(width, height, Transparency.TRANSLUCENT);
            } else {
                image = clearImage(image);
            }
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.drawImage(visibleMask, 0, 0, null);
            } finally {
                g2d.dispose();
            }
            for (int y = 0; y < image.getHeight(); y++) {
                for (int x = 0; x < image.getWidth(); x++) {
                    final int source = image.getRGB(x, y);
                    final int alpha = (source >> 24) & 0xFF;
                    final int red = (source >> 16) & 0xFF;
                    final int green = (source >> 8) & 0xFF;
                    final int blue = (source >> 0) & 0xFF;
                    final int destination = (255 - alpha) << 24 | (red << 16) | (green << 8) | (blue << 0);
                    image.setRGB(x, y, destination);
                }
            }
            return image;
        }
     
     
        private BufferedImage renderFogOfWar(BufferedImage image, final int width, int height) {
            if (image == null) {
                image = createAcceleratedImage(width, height, Transparency.TRANSLUCENT);
            } else {
                image = clearImage(image);
            }
            final var g2d = setupGraphics(image.createGraphics());
            try {
                g2d.drawImage(fogOgWarMask, 0, 0, null);
                g2d.setComposite(AlphaComposite.SrcIn);
                g2d.setPaint(FOG);
                g2d.fill(playground);
            } finally {
                g2d.dispose();
            }
            return image;
        }
     
        private void exportLayers() {
            Optional.ofNullable(visibleMask)
                    .ifPresent(image -> exportImage(image, "fow-mask-visible.png"));
            Optional.ofNullable(fogOgWarMask)
                    .ifPresent(image -> exportImage(image, "fow-mask-fog-of-war.png"));
            Optional.ofNullable(defaultBackground)
                    .ifPresent(image -> exportImage(image, "layer-background-default.png"));
            Optional.ofNullable(visibleBackground)
                    .ifPresent(image -> exportImage(image, "layer-background-visible.png"));
            Optional.ofNullable(fogOfWar)
                    .ifPresent(image -> exportImage(image, "layer-fog-of-war.png"));
            // Test: make sure clearImage really clears content.
            Optional.ofNullable(defaultBackground)
                    .ifPresent(image -> {
                        var image2 = createAcceleratedImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
                        final var g2d = setupGraphics(image.createGraphics());
                        try {
                            g2d.setPaint(Color.RED);
                            g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
                        } finally {
                            g2d.dispose();
                        }
                        image2 = clearImage(image2);
                        exportImage(image2, "test-clear.png");
                    });
        }
    }
    Nom : Untitled.jpg
Affichages : 331
Taille : 28,4 Ko

    Comme ca fait pas mal de temps que je ne fais plus de Java2D assez soutenu, j'ai du replonger le nez dans qq bouquins notamment "Filthy Rich Clients - Developing Animated and Graphical Effects for Desktop Java Applications" de Chet Haase et Romain Guy. Des effect bien plus complexes que ce que j'ai fait ici sont possibles dont des effects / shaders qui impactent les valeurs des pixels ce qui permet de renforcer les éclairages par exemple (en faisant des effets de light, multiply, burn ou autre comme peuvent le faire les logiciels de dessin qui fonctionnent avec des calques). En combinant le tout avec des composites ont peut faire des scenes 2D assez chiadées.

    PS : l’énorme cadre noir est du au gros blur que je fais sur l'image de fond. Plusieurs manières possibles de contourner ce soucis (parmi d'autres sans doute) :
    • Utiliser une image de fond plus grosse qui dépasse de la zone a l’écran de tous les cotés (bof bof = sur-consommation mémoire).
    • Faire le blur directement sur la mini image servant de source a la texture (mais des soucis de raccord dans la texture peuvent apparaître lorsqu'on blur trop).
    • Créer une 1ere image temporaire 3 x 3 (ou plus) la taille de la texture, faire un blur et en extraire la partie centrale pour créer la texture definitive.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  6. #6
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut
    Énorme, merci beaucoup.
    J'ai déjà utilisé les composites mais comme le résultat ramait énormément j'en ai conclus que ce n'était pas fait pour les animations.
    Par contre il y a beaucoup de truc que je n'ai pas compris dans le code.

    visibleMask, fogOgWarMask, defaultBackground, visibleBackground, fogOfWar
    Ça représente quoi à l'écran?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    addComponentListener(new ComponentAdapter() {
    public void componentResized(ComponentEvent event) {
    Ça sert à quoi? Qu'est ce que tu peut faire du ComponentEvent?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    private static BufferedImage createAcceleratedImage(final int width, final int height, final int transparency) {
    final var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    final var gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
    return gc.createCompatibleImage(width, height, transparency);
    }
    A quoi ça sert? Comment ça marche?

    Quel est la parti du code qui "trou" le brouillard de guerre pour donner la visibilité?

    Quel est la partie du code qui floute le terrain?

    Merci beaucoup de ton aide.
    Ça m'apporte beaucoup et ça me fait super plaisir que tu ai pris autant temps pour répondre.
    Des jours c'est facile, des jours c'est pas facile, mais c'est jamais le même jour.

  7. #7
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Rappel, dans une composite :
    • La destination c'est ce qui est deja dans l'image
    • La source c'est ce qu'on dessine dans l'image


    Pas fan de la terminologie mais bon c'est comme ca...

    Je vais mettre les images mais Developpez va afficher du blanc ou du noir opaque au lieu du damier actuel qu'on peut voir dans les logiciels de dessin ou de manipulation d'image donc du coup certains effets de transparence vont être moins facile a saisir au premier abord.

    • defaultBackground c'est ce qui affiche hors du champs de vu de l'utilisateur. par exemple dans certains jeux c'est une vue du monde avec une texture "terra incognita" ou sans details (on ne voit pas les positions ou déplacements ennemis) ou encore en niveau de gris (tandis que la zone visible est colorée).
      Dans notre cas, c'est une image de la taille de toute la zone qui a une texture de carre bleu et sur laquelle on a applique un floutage. C'est l'image la moins recalculee de la demo, la seule fois ou on la reconstruit c'est lorsqu'on change les dimensions de la fenêtre.

      Nom : layer-background-default.png
Affichages : 324
Taille : 31,5 Ko
    • visibleMask c'est le cone de vue de l'utilisateur un arc avec un gradient de blanc -> transparent (lorsqu'on fera la composite la zone opaque blanche affichera la destination tandis que la zone transparente affichera la source). Ici l'image fait toute la zone affichable, y a probablement moyen d'optimiser ca en se recentrant sur la boite englobante du cone.

      Nom : fow-mask-visible.png
Affichages : 296
Taille : 13,4 Ko
    • visibleBackground c'est ce qui affiche dans le champs de vu de l'utilisateur. C'est une image de la taille de toute la zone, ici aussi il y a moyen de mieux faire avec une image plus petite.
      On la compose de la manière suivante :
      • L'image est initialement totalement transparente.
      • On y dessine le masque visible (donc on se retrouve avec le cone en degrade de blanc au centre d'une image totalement transparente) c'est notre destination.
      • On se met en composite SrcIn : on affichera uniquement les pixels de la source qui se trouvent dans la destination.
      • On remplit une zone avec la texture des rectangles bleus (qui sont nets pas de flou), c'est notre source.

      Donc dans l'image finale la ou c’était transparent au depart ca l'est toujours. La ou c'etait totalement opaque au depart on voit des carres bleus. Et la ou c'etait transparent ben on a des carres bleus qui vont transparent plus on va vers l'extremite du cone.

      Nom : layer-background-visible.png
Affichages : 311
Taille : 9,3 Ko
    • fogOgWarMaskIci c'est un peu l'inverse de notre masque visible, c'est a dire que c'est le masque ou doit d'afficher le brouillard de guerre. Donc c'est une image qui doit être opaque dans toute la zone en dehors du cone. Donc ce que j'ai fait c'est de prendre l'image visibleMask et d'inverser le canal alpha sur tous les pixels alpha dst = 255 - alpha src. Comme l'image est managed après l'opération (on a tapé directement dans les pixels) on la retransforme en image "accélérée".

      Nom : fow-mask-fog-of-war.png
Affichages : 296
Taille : 13,8 Ko
    • fogOfWar littéralement le brouillard de guerre. Dans certains jeu tu peux avoir un calque qui contient des nuages ou des effets de brouillard (qui peuvent être animes) ou encore qui assombri la carte (quand on est a l’intérieur d'un donjon par exemple). Donc j'ai decide de faire un calque supplémentaire pour cela pour simuler un interieur sombre:
      On la compose de la manière suivante :
      • L'image est initialement totalement transparente.
      • On y dessine le masque fog of war (donc on se retrouve une image totalement noire opaque avec au centre une partie evidee semi-transparente qui correspond au cone de vue) c'est notre destination.
      • On se met en composite SrcIn : on affichera uniquement les pixels de la source qui se trouvent dans la destination.
      • On remplit une zone avec une couleur sombre translucide, c'est notre source.

      on se retrouve donc avec un calque sombre semi-transparent mais évidé dans la partie du cone de vision.
      Nom : layer-fog-of-war.png
Affichages : 304
Taille : 8,2 Ko


    la scene finale est la composition des divers calques + le cercle rouge qui représente le joueur.

    PS : la composite Clear supprime l’intégralité de la destination et de la source qui se chevauchent, c'est donc un moyen efficace de mettre tous les pixels d'une image a la couleur transparence sans devoir faire un (setRBG() qui est operation coûteuse et non efficace en plus de faire que l'image devienne managed).

    Le ComponentListener est nécessaire car si on redimensionne la fenêtre plusieurs images ont besoin d’être re-crées pour avoir les bonnes tailles.

    Swing (mais aussi ImageIO lorsqu'on charge des fichiers depuis le disque) par défaut utilise des images managed c'est a dire sans support d'acceleration de la carte graphique. Créer des images "accélérées" permet d'instancier des images stockées dans la mémoire video et dont le format interne des pixels est compatible avec la carte graphique et donc ainsi de bénéficier d'un boost non-négligeable lors des animations ou composition de scene complexe. Par contre des qu'on tape dans le raster d'une BufferedImage (soit en accès direct via manipulation du raster soit par setRGB/getRGB() soit par une ImageOp quelconque), l'image redevient managed. Il convient donc alors de recreer une image "accélérée" et de recopier l'image managed dedans.

    Dans le code tu as 2 operations ConvolveOp qui sont deux floutages BOX_BLUR_5 qui est utilisé pour adoucir les bords du cone de vue et BOX_BLUR_20 qui est utilisé pour flouter le terrain. Le code de cette opération provient du livre "Filthy Rich Client", j'aurai pu aussi faire un gaussian blur mais le code est plus long a réécrire et l’opération est aussi plus coûteuse (en temps) pour un gain de qualité de floutage pas vraiment evident pour une telle demo. Souvent box blur permet d'avoir un résultat assez similaire pour l'oeil non averti pour un coût (en temps) moindre.
    • le blur 5 est applique trop souvent alors que le cone ne change jamais, j'aurai tout aussi bien pu creer une petite image du cone et la blur et ensuite la faire tourner au bon angle pour la composer dans la scene.
    • le blur 20, j'ai ete feneant, c'est probablement assez couteux comme operation (grosse matrice), j'aurai sans doute pu obtenir le meme resultant en appliquant plusieurs fois le blur 5.


    Parmi les autres optimisations possibles il y a aussi le fait que la texture des rectangles bleus nets est redessinée en live lors de la creation de visibleBackground. Ca serait sans doute mieux de la precalculer dans une image. Idem pour le cone de vue. De manière générale, c'est plus rapide de composer des bitmaps entres elles plutôt que de dessiner des textures ou du vectoriel. Apres c'est un échange temps CPU (vectoriel + effets) <-> mémoire (bitmaps) donc quand on beaucoup de bitmaps il faut leur donner une taille efficace, etc. Une autre astuce utilisée du temps des jeux 8-bit/16-bit c'est de faire un cone de vue en trame plutôt qu'en degrade (intercalage de pixels opaques et complètement transparents) ca permet de faire des operations plus rapides mais aussi d'avoir un masque qui prend moins de place si on doit le stocker sur le disque (image N/B au lieu de image en 255 niveaux de gris ou pire images en 24-bit/32-bit couleur).

    Apres ce sont des notions d'optimisation propres au jeu video 2D et c'est pas mon domaine, quand on reste dans des interfaces 2D de bureau on peut quand meme faire qq jolis effets sans devoir faire des optimisation de ouf.
    Dans le cadre de "Filty Rich Client" SrcIn était utilisée par exemple pour créer des fenêtre Swing de forme biscornues.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 24/02/2009, 12h06
  2. Comment utiliser un cache ?
    Par TOM-Z dans le forum XMLRAD
    Réponses: 4
    Dernier message: 14/03/2003, 09h55
  3. comment utiliser actionscript ?
    Par webs dans le forum Flash
    Réponses: 3
    Dernier message: 09/02/2003, 23h11
  4. Comment utiliser OUT ?
    Par Bouziane Abderraouf dans le forum CORBA
    Réponses: 3
    Dernier message: 20/07/2002, 09h35
  5. Réponses: 5
    Dernier message: 11/06/2002, 15h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo