代码之家  ›  专栏  ›  技术社区  ›  JarsOfJam-Scheduler

“末日之火是如何完成的”—重新实现一个能产生噪音动画的火动画

  •  0
  • JarsOfJam-Scheduler  · 技术社区  · 6 年前

    (注:本问题末尾提供了一个最小、完整和可验证的示例)

    总结

    1. 背景、目标和问题

    2. 我的(可能是错的?)理解作者的解释和来源 -注意:我必须向您解释,因为我的错误可能来自于对该做什么的错误理解,或者我的代理执行错误…

    3. 我所做的

    4. 预期结果、实际结果和问题

    5. 最小、完整和可验证的示例

    背景、目标和问题

    我研究的动画著名的厄运游戏根据 http://fabiensanglard.net/doom_fire_psx/ 它包括生火。

    作者代码的完整版本是: https://github.com/fabiensanglard/DoomFirePSX/blob/master/flames.html

    我以这个实现结束,但我得到的结果是一个噪声动画(这就是问题所在)。实际上,在这个动画的最后,我得到了这个结果:

    enter image description here


    我的(可能是错的?)理解作者的解释和来源

    第一种方法:实施主要原则,简化火势(因为用简单的渐变色代替)

    1. 设置一个精确填充的颜色集:该集定义一个看起来像火的渐变。从白色到黑色有36种颜色,其中有黄色、橙色和红色。此集合不包含任何重复项。

    2. 在画布像素上首次迭代。这里的目标是将所有像素都涂成黑色(即:集合的最后一种颜色)。

    3. 在画布上重复第二次。这次,我们必须将底部第一行的像素(即集合的第一个颜色)涂成白色。

    4. 在画布上再次迭代,但只从第二条底线(包括在内)开始,而不是从第一条底线(因此排除在外)开始。对于每一个迭代的像素,我们以如下方式修改它的颜色:我们取其直接较低像素的颜色,我们在所有颜色中找到该颜色的索引,我们向该索引添加1:我们获得另一个名为I2的索引,我们找到索引为I2的颜色,然后将该颜色应用于该迭代的像素。

    一旦执行完成,将有几个渐变,每个渐变包含36行(36色)。

    让它看起来像火。

    程序解释人 http://fabiensanglard.net/doom-fire-psx/ 当然,比这更进一步:它使用伪随机两次,得到一些看起来不像简单梯度,但看起来像火的东西。

    想法如下。对于正在迭代的像素:

    我们得到了下面像素的颜色索引。然后,我们得到颜色,在颜色集中,它的索引是这个索引+一个涉及轻微偏移的随机数(如果我记得正确,最多2个平方)。因此,我们可以建立粒子温度变化的加速度模型。

    此外,我们还考虑迭代像素左侧一点的像素。”一点点“=根据1号芯片的相同随机数。正是这个位于左边一点的像素将被分配到n_°1芯片中恢复的颜色。因此,我们可以模拟火焰左侧的水平位移。

    所以我们看到它是一个三角形的功(因为我们取了被迭代的像素,下面的一个,左边的一个)。

    模拟粒子温度变化加速度的伪随机方法

    生成一个介于0和3之间的随机数,并在此处使用:

    firePixels[src - FIRE_WIDTH ] = pixel - (rand & 1);

    因此,将采用的颜色略有变化。

    伪随机用于模拟火焰左侧的水平位移。

    除了我们刚才看到的,在像素上使用伪随机,每行也使用。

    这里重复使用相同的随机数:

    var dst = src - rand + 1; firePixels[dst - FIRE_WIDTH ] = pixel - (rand & 1);

    在这里,水平方向作了轻微的移动。


    我所做的

    上面的解释都已经实现了,但是我的程序输出的结果不好。所以:

    1. 或者我不太明白这个想法,

    2. 或者我做得不好。

    您将在下面找到实现的源代码。

    预期结果、实际结果和问题

    我希望有几个垂直梯度(每个从下到上)。”几个“因为我的画布的高度大于我的渐变颜色的数量,而且因为我使用一个模块来选择要应用的颜色。这些梯度一定像诅咒的梯度。( http://fabiensanglard.net/doom-fire-psx/ )

    实际结果是:我得到了几个噪音。

    我的问题是:为什么不起作用?我想我很明白该怎么做。也许我忘了在实现中使用something,但是什么?

    我的问题是:为什么不起作用?我想我很明白该怎么做。也许我忘了在实现中使用something,但是什么?

    最小、完整和可验证的示例

    爪哇

    import java.util.ArrayList;
    
    public class Launcher {
    
        public static void main(String args[]) {
            int width = 800, height = 800;
            Gui gui = new Gui(width, height);
            gui.setUp("DOOM-like fire");
            gui.setVisible(true);
    
            Colors colors = new FireColors(new ArrayList<>());
            gui.colorize(colors.getLastColor(), -1, -1);  // Setting black anywhere
            gui.colorize(colors.getColorAtIndex(0), -1, height - 1);  // Setting white, in a lower line
    
            new ThreadPainter(0, colors, gui, width, height).schedulePainting();
        }
    
    }
    

    爪哇

    import java.awt.*;
    import javax.swing.*;
    import java.awt.image.BufferedImage;
    
    class Gui extends JFrame {
    
        private JPanel panel;
        private BufferedImage buffered_image;
    
        Gui(int width, int height) {
            buffered_image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            panel = new JPanel() {
                public void paintComponent(Graphics graphics) {
                    super.paintComponent(graphics);
                    graphics.drawImage(buffered_image, 0, 0, null);
                }
            };
        }
    
        void setUp(String title) {
            setTitle(title);
            setLayout(null);
            setSize(buffered_image.getWidth(), buffered_image.getHeight());
            setContentPane(panel);
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        }
    
        void colorize(Color color, int x_parameter, int y_parameter) {
            for(int y = (y_parameter == -1 ? 0 : y_parameter); y <= (y_parameter == -1 ? this.getHeight() - 1 : y_parameter); y++) {
                for(int x = (x_parameter == -1 ? 0 : x_parameter); x <= (x_parameter== -1 ? this.getWidth() - 1 : x_parameter); x++) {
                    buffered_image.setRGB(x, y, color.getRGB());
                }
            }
            panel.repaint();
        }
    
        int getRGBAtCoordinates(int x, int y) {
            return buffered_image.getRGB(x, y);
        }
    
    }
    

    线程指针.java

    import java.util.Timer;
    import java.util.TimerTask;
    
    class ThreadPainter extends Timer {
        private Gui gui;
        private Colors colors;
        private int delay;
        private int height;
        private int width;
    
        ThreadPainter(int delay, Colors colors, Gui gui, int width, int height) {
            this.colors = colors;
            this.gui = gui;
            this.delay = delay;
            this.width = width;
            this.height = height;
        }
    
        void schedulePainting() {
            this.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        int number_of_colored_portions = height / colors.getSize();
                        // int locking_changement_of_color = 0;
                        for(int y = height - 2; y >= 0; y--) {
    
                            //if(locking_changement_of_color == number_of_colored_portions) {
                                //locking_changement_of_color = 0;
                            /*} else {
                                index_of_color_to_apply = index_of_found_color;
                            }*/
    
                            for(int x = 0; x < width; x++) {
                                int rand = (int) Math.round(Math.random() * 3.0) & 3;
                                int below_pixel_rgb = gui.getRGBAtCoordinates(x, y + 1);
                                int index_of_found_color = colors.getIndexOfColor(below_pixel_rgb);
                                int index_of_color_to_apply = (index_of_found_color + (rand & 1)) % colors.getSize();
    
                                int x_copy = x - rand + 1;
                                if(x_copy <= 0) {
                                    x_copy = 0;
                                } else if(x_copy >= width) {
                                    x_copy = width - 1;
                                }
                                gui.colorize(colors.getColorAtIndex(index_of_color_to_apply), x_copy, y);
                            }
    
                            //locking_changement_of_color++;
                            //Thread.sleep(10);
                        }
                    } catch(Exception e) {
                        System.err.println("Exception: " + e.getMessage());
                    }
                }
            }, this.delay);
        }
    
    }
    

    爪哇

    import java.awt.Color;
    import java.util.List;
    
    abstract class Colors {
        List<Color> colors;
    
        Color getColorAtIndex(int index) {
            return colors.get(index);
        }
    
        int getIndexOfColor(int rgb) throws Exception {
            for (int x = 0; x < colors.size(); x++) {
                if(colors.get(x).getRGB() == rgb) {
                    return x;
                }
            }
            throw new Exception("Color not found in the list!");
        }
    
        int getSize() {
            return colors.size();
        }
    
        Color getLastColor() {
            return colors.get(colors.size() - 1);
        }
    }
    

    FielLo.java

    import java.awt.Color;
    import java.util.List;
    
    class FireColors extends Colors {
    
        FireColors(List<Color> colors) {
    
            this.colors = colors;
    
            this.colors.add(new Color(255, 255, 255));
            this.colors.add(new Color(239, 239, 199));
            this.colors.add(new Color(223, 223, 159));
            this.colors.add(new Color(207, 207, 111));
            this.colors.add(new Color(183, 183, 55));
            this.colors.add(new Color(183, 183, 47));
            this.colors.add(new Color(183, 175, 47));
            this.colors.add(new Color(191, 175, 47));
            this.colors.add(new Color(191, 167, 39));
            this.colors.add(new Color(191, 167, 39));
            this.colors.add(new Color(191, 159, 31));
            this.colors.add(new Color(191, 159, 31));
            this.colors.add(new Color(199, 151, 31));
            this.colors.add(new Color(199, 143, 23));
            this.colors.add(new Color(199, 135, 23));
            this.colors.add(new Color(207, 135, 23));
            this.colors.add(new Color(207, 127, 15));
            this.colors.add(new Color(207, 119, 15));
            this.colors.add(new Color(207, 111, 15));
            this.colors.add(new Color(215, 103, 15));
            this.colors.add(new Color(215, 95, 7));
            this.colors.add(new Color(223, 87, 7));
            this.colors.add(new Color(223, 87, 7));
            this.colors.add(new Color(223, 79, 7));
            this.colors.add(new Color(199, 71, 7));
            this.colors.add(new Color(191, 71, 7));
            this.colors.add(new Color(175, 63, 7));
            this.colors.add(new Color(159, 47, 7));
            this.colors.add(new Color(143, 39, 7));
            this.colors.add(new Color(119, 31, 7));
            this.colors.add(new Color(103, 31, 7));
            this.colors.add(new Color(87, 23, 7));
            this.colors.add(new Color(71, 15, 7));
            this.colors.add(new Color(47, 15, 7));
            this.colors.add(new Color(7, 7, 7));
    
        }
    
    }
    
    1 回复  |  直到 6 年前
        1
  •  3
  •   Iłya Bursov    6 年前

    有很多小问题,所以这里有一个带注释的固定版本供您理解(为了简化,我将类组合成一个文件):

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.image.BufferedImage;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.Timer;
    import java.util.TimerTask;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.WindowConstants;
    
    public class Launcher {
        private static class Gui extends JFrame {
            final int width;
            final int height;
            final JPanel panel;
            final BufferedImage buffered_image;
    
            Gui(final String title, final int width, final int height) {
                this.width = width;
                this.height = height;
                this.buffered_image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                this.panel = new JPanel() {
                    @Override
                    public void paintComponent(final Graphics graphics) {
                        super.paintComponent(graphics);
                        graphics.drawImage(Gui.this.buffered_image, 0, 0, null);
                    }
                };
                this.setTitle(title);
                this.setContentPane(this.panel);
                this.setSize(width, height);
                this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                this.setVisible(true);
            }
    
            void colorize(final Color color, final int x, final int y) {
                if ((x < 0) || (x >= this.width) || (y < 0) || (y >= this.height))
                    return;
                this.buffered_image.setRGB(x, y, color.getRGB());
            }
    
            int getRGBAtCoordinates(final int x, final int y) {
                return this.buffered_image.getRGB(x, y);
            }
        }
    
        public static void main(final String args[]) {
            final List<Color> colors = new ArrayList<>();
            colors.add(new Color(0, 0, 0)); // black
            colors.add(new Color(7, 7, 7));
            colors.add(new Color(47, 15, 7));
            colors.add(new Color(71, 15, 7));
            colors.add(new Color(87, 23, 7));
            colors.add(new Color(103, 31, 7));
            colors.add(new Color(119, 31, 7));
            colors.add(new Color(143, 39, 7));
            colors.add(new Color(159, 47, 7));
            colors.add(new Color(175, 63, 7));
            colors.add(new Color(191, 71, 7));
            colors.add(new Color(199, 71, 7));
            colors.add(new Color(223, 79, 7));
            colors.add(new Color(223, 87, 7));
            colors.add(new Color(223, 87, 7));
            colors.add(new Color(215, 95, 7));
            colors.add(new Color(215, 103, 15));
            colors.add(new Color(207, 111, 15));
            colors.add(new Color(207, 119, 15));
            colors.add(new Color(207, 127, 15));
            colors.add(new Color(207, 135, 23));
            colors.add(new Color(199, 135, 23));
            colors.add(new Color(199, 143, 23));
            colors.add(new Color(199, 151, 31));
            colors.add(new Color(191, 159, 31));
            colors.add(new Color(191, 159, 31));
            colors.add(new Color(191, 167, 39));
            colors.add(new Color(191, 167, 39));
            colors.add(new Color(191, 175, 47));
            colors.add(new Color(183, 175, 47));
            colors.add(new Color(183, 183, 47));
            colors.add(new Color(183, 183, 55));
            colors.add(new Color(207, 207, 111));
            colors.add(new Color(223, 223, 159));
            colors.add(new Color(239, 239, 199));
            colors.add(new Color(255, 255, 255)); // white
    
            final Gui gui = new Gui("DOOM-like fire", 800, 800);
    
            final Color black = colors.get(0);
            final Color white = colors.get(colors.size() - 1);
            final Dimension dim = gui.getContentPane().getSize(); // get actual size, without title/borders
            for (int y = 0; y < dim.height; y++) {
                final Color clr = y < (dim.height - 1) ? black : white;
                for (int x = 0; x < dim.width; x++)
                    gui.colorize(clr, x, y);
            }
    
            new Timer().schedule(new TimerTask() {
                final Random rnd = new Random();
    
                Color getColorAtIndex(final int index) {
                    if (index < 0)
                        return colors.get(0); // minimal color is black
                    return colors.get(index);
                }
    
                int getIndexOfColor(final int rgb) {
                    for (int x = 0; x < colors.size(); x++)
                        if (colors.get(x).getRGB() == rgb)
                            return x;
                    throw new RuntimeException("Color not found in the list!");
                }
    
                @Override
                public void run() {
                    for (int x = 0; x < dim.width; x++) {
                        for (int y = 1; y < dim.height; y++) {
                            final int new_index = this.getIndexOfColor(gui.getRGBAtCoordinates(x, y)) - this.rnd.nextInt(2);
                            final int new_x = (x - this.rnd.nextInt(3)) + 1;
                            gui.colorize(this.getColorAtIndex(new_index), new_x, y - 1);
                        }
                    }
                    gui.repaint();
                }
            }, 0, 40); // start immediately and repeat every 40ms
        }
    }