噪声纹理

现在学习到了“噪声”部分,感觉书上讲解地比较浅显,我对Perlin噪声的原论文比较感兴趣。下面是原文的连接:

http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

由于众所周知的问题,国内无法访问这个地址。:(

于是,我又辗转找到了這篇论文。

http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf

此文的作者主要目的是介绍Perlin发明的Simplex Noise算法,同时也介绍了经典Perlin Noise算法。写得很清楚,还有代码例子。

http://weber.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java

基于这个代码,我生成了简单的噪声纹理。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * 基于SimplexNoise,生成噪音纹理。
 * @author yanmaoyuan
 *
 */
public class NoiseMap extends JFrame {

    private static final long serialVersionUID = -5634742336049921860L;

    // 图像分辨率
    private int width = 192;
    private int height = 192;
    
    // 缩放系数
    private double scale = 4.0;
    
    // 图像缓冲
    private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
    
    private JPanel panel = null;
    
    public NoiseMap() {
        // 更新噪音纹理
        updateNoiseMap();
        
        // 创建一个JPanel,用于显示噪音纹理。
        panel = new JPanel() {
            private static final long serialVersionUID = -5646436871841183655L;

            @Override
            public void update(Graphics g) {
                paint(g);
            }

            @Override
            public void paint(Graphics g) {
                g.drawImage(image, 0, 0, width, height, 0, 0, width, height, null);
            }
        };
        
        panel.setPreferredSize(new Dimension(width, height));
        getContentPane().add(panel, BorderLayout.CENTER);
        
        // 用于调节缩放系数。
        final JSlider slider = new JSlider();
        slider.setMaximum(width);
        slider.setMinimum(1);
        slider.setValue((int)scale);
        slider.setPaintTicks(true);
        slider.setMajorTickSpacing(16);
        slider.setMinorTickSpacing(4);
        slider.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                int v = slider.getValue();
                scale = v;
                updateNoiseMap();
                panel.repaint();
            }
            
        });
        
        this.getContentPane().add(slider, BorderLayout.NORTH);
        
        // 用于导出纹理贴图
        JButton btn = new JButton("导出");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    String name = String.format("noise_%d.png", (int)scale);
                    ImageIO.write(image, "png", new File(name));
                } catch (Exception ex) {}
                
            }
        });
        this.getContentPane().add(btn, BorderLayout.SOUTH);
        
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        
    }
    
    /**
     * 重绘噪声图
     */
    public void updateNoiseMap() {
        Graphics g = image.getGraphics();
        
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                double noise = SimplexNoise.noise(x/scale, y/scale);
                int color = (int)(128 * (noise + 1));
                g.setColor(new Color(color, color, color));
                g.drawOval(x, y, 1, 1);
            }
        }
        g.dispose();
    }
    
    public static void main(String[] args) throws IOException {
        NoiseMap app = new NoiseMap();
        app.setVisible(true);
    }

}

生成不同缩放倍率的噪音纹理如下:

1倍噪音

2倍噪音

4倍噪音

8倍噪音

16倍噪音

32倍噪音

64倍噪音

128倍噪音

很多人使用噪音纹理来制作2D云。

例如**冯乐乐(aka candycat1992)**的:【Unity Shader】2D动态云彩

文中用到了Tileable perlin noise,实现的思路很有趣。

根据这篇文章的思路,我用jME3实现了一个2D动态云彩。从技术上来说,看起来还不错。

不过目前Tileable Noise的问题还没解决,所以实际运行时会有裂缝。

很羡慕这个女孩子,92年的,上交大软件工程专业的研究生,还没工作时就已经出书了。

这几天反复阅读 The Real-time Volumetric Cloudscapes of Horizon - Zero Dawn - ARTR.pdf,总是忍不住赞叹老外的思维。以我现在的能力还无法实现文章中描述的技术,所以我想先做个笔记。等以后实力足够了,我再尝试在jME3中实现这种技术。

文中提到,他们使用Worley Noise和Perlin Noise结合,形成Worley-Perlin Nosie,用来生成花包菜状的云朵。下面是关于Worley Noise在GLSL中实现的论文:

http://weber.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf

效果预览:http://erkaman.github.io/glsl-worley/

来源:http://www.itn.liu.se/~stegu/