柏林噪声产生火焰等纹理

柏林噪声是一种特殊的随机噪声,即对于每个给定的值产生的随机数是唯一的,但是不同的

值产生不同的随机数。关于柏林噪声更详细的解释可以参考这里:

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

 

本文主要是探讨如何使用柏林噪声产生火焰效果与乌云效果的纹理,在解释实现代码之前,

首先来看一下影响柏林噪声效果的两个参数音度(Octave) 与余辉(Persistence),可以调节

纹理的大小和密度。

 

最终实现的火焰纹理效果


最终实现的乌云纹理效果


最终实现的草地纹理效果–怎么感觉有点魔兽中精灵族的草地啊,哈哈


代码解释

首先产生随机空白噪声,使用随机空白噪声产生柏林噪声,最后将柏林噪声映射为RGB值

输出到指定大小的图像中,代码如下:

float[][] whiteNoise = GenerateWhiteNoise(rows, cols);

float[][] perlinNoise = GeneratePerlinNoise(whiteNoise, 6); //

float[][] colorData = MapGray(perlinNoise);

 

白噪声产生主要是利用JAVA中的系统时间作为种子,产生[0~1]之间的噪声数组

代码如下:

public float[][] GenerateWhiteNoise(int width, int height)

{

Random random = new Random(System.currentTimeMillis());    

float[][] noise = new float[width][height];

for (int i = 0; i < width; i++)

{

        for (int j = 0; j <height; j++)

        {

            noise[i][j] = (float)random.nextDouble();

        }

    }

    return noise;

}

 

柏林噪声的产生稍微复杂一点点,首先把上面的白噪声数据带入,利用插值公式产生平滑的噪声

数据,具体要产生几组平滑噪声数据取决于音度(Octave)参数。本程序的插值公式非常简单,

代码如下:

public float Interpolate(float x0, float x1, float alpha)

{

return x0 * (1 - alpha) + alpha * x1;

}

最后把这些组的平滑噪声加上不同的振幅混合在一起产生一个输出数组结果即为柏林噪声。

完成上面这些还不足以产生那些效果震撼的纹理,另外一个顶级秘诀在于怎么把柏林噪声

映射到你想要的RGB值。代码如下:

    float[][] MapGradient(float[][] perlinNoise)

    {

       int width =perlinNoise.length;

       int height =perlinNoise[0].length;

       float[][] image = new float[width][height];

       int ta=0, tr=0, tb=0,tg=0;

       for (int i = 0; i <width; i++)

       {

          for (int j = 0; j <height; j++)

          {

          ta = 255;

          int u = (int)(perlinNoise[i][j] * (float)angryFireColorTable.length);

          tr = (int)angryFireColorTable[u][0];

          tg = (int)angryFireColorTable[u][1];

          tb = (int)angryFireColorTable[u][2];

          image[i][j] = (ta <<24) | (tr << 16) | (tg << 8) | tb;

          }

       }

      

       return image;

    }

程序完全源代码如下:

package com.gloomyfish.perlin.noise;

import java.util.Random;

public class PerlinNoiseCreator {
	
	private int[][] angryFireColorTable = {
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},
			{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 204},	{255, 255, 199},	
			{255, 255, 199},	{255, 255, 197},	{255, 255, 197},	{255, 255, 193},	{255, 255, 193},
			{255, 255, 191},	{255, 255, 191},	{255, 255, 189},	{255, 255, 189},	{255, 255, 185},
			{255, 255, 185},	{255, 255, 183},	{255, 255, 183},	{255, 255, 179},	{255, 255, 179},
			{255, 255, 177},	{255, 255, 177},	{255, 255, 175},	{255, 255, 175},	{255, 255, 171},		
			{255, 255, 171},	{255, 255, 169},	{255, 255, 169},	{255, 255, 167},	{255, 255, 167},
			{255, 255, 163},	{255, 255, 161},	{255, 255, 157},	{255, 255, 155},	{255, 255, 153},
			{255, 251, 149},	{255, 249, 147},	{255, 246, 144},	{255, 244, 142},	{255, 242, 140},
			{253, 244, 205},	{248, 246, 197},	{248, 246, 187},	{248, 245, 178},	{248, 245, 168},
			{247, 245, 160},	{248, 243, 149},	{247, 244, 141},	{249, 243, 133},	{248, 243, 123},
			{249, 242, 112},	{248, 242, 102},	{248, 242, 92},	{247, 241, 81},	{248, 241, 73},
			{247, 240, 63},	{249, 239, 53},	{247, 239, 42},	{249, 238, 32},	{249, 238, 26},
			{248, 234, 21},	{248, 231, 21},	{250, 224, 25},	{248, 218, 24},	{249, 214, 26},
			{249, 209, 26},	{252, 204, 32},	{251, 198, 32},	{251, 191, 33},	{251, 186, 34},
			{250, 179, 35},	{252, 176, 38},	{252, 169, 41},	{252, 164, 41},	{254, 157, 44},
			{254, 151, 46},	{253, 145, 47},	{254, 141, 49},	{251, 136, 47},	{253, 135, 48},
			{251, 130, 47},	{250, 129, 46},	{249, 126, 46},	{247, 124, 44},	{246, 120, 43},
			{244, 118, 41},	{243, 115, 42},	{241, 113, 40},	{242, 111, 41},	{240, 109, 39},
			{239, 104, 40},	{236, 101, 37},	{234, 99, 35},	{235, 97, 34},	{232, 93, 34},
			{231, 91, 32},	{229, 88, 32},	{227, 86, 30},	{227, 83, 30},	{225, 81, 28},
			{224, 78, 27},	{222, 76, 25},	{223, 72, 27},	{221, 70, 25},	{219, 66, 24},
			{216, 63, 22},	{216, 58, 21},	{212, 54, 19},	{210, 50, 18},	{209, 45, 17},
			{206, 40, 14},	{206, 37, 14},	{203, 32, 12},	{200, 29, 9},	{200, 24, 9},
			{197, 21, 6},	{195, 17, 7},	{191, 13, 3},	{190, 7, 3},	{188, 5, 1},
			{184, 2, 0},	{180, 0, 0},	{178, 0, 0},	{174, 0, 0},	{172, 0, 0},
			{169, 1, 0},	{164, 0, 1},	{160, 0, 0},	{158, 0, 0},	{154, 0, 0},
			{150, 0, 0},	{146, 0, 0},	{144, 0, 0},	{140, 0, 1},	{136, 0, 2},
			{133, 0, 1},	{130, 0, 0},	{126, 1, 0},	{124, 0, 2},	{120, 0, 1},
			{116, 0, 0},	{112, 0, 0},	{109, 1, 1},	{104, 0, 0},	{103, 0, 1},
			{98, 0, 0},	{95, 0, 0},	{92, 1, 0},	{92, 1, 0},	{90, 0, 0},
			{89, 1, 0},	{88, 0, 0},	{86, 0, 0},	{86, 0, 0},	{84, 0, 0},
			{84, 0, 0},	{82, 1, 0},	{82, 1, 0},	{80, 0, 0},	{80, 0, 0},
			{79, 1, 1},	{78, 0, 0},	{76, 0, 0},	{76, 0, 0},	{74, 0, 0},
			{74, 0, 0},	{72, 0, 1},	{72, 0, 1},	{70, 0, 0},	{70, 0, 0},
			{69, 1, 2},	{68, 0, 1},	{66, 0, 1},	{66, 0, 1},	{64, 0, 0},
			{62, 1, 0},	{61, 1, 1},	{60, 0, 0},	{60, 0, 0},	{60, 0, 0},
			{58, 0, 0},	{58, 0, 0},	{56, 0, 1},	{56, 0, 1},	{54, 0, 0},
			{54, 0, 0},	{52, 1, 0},	{51, 0, 0},	{50, 0, 1},	{50, 0, 1},
			{49, 1, 1},	{48, 0, 0},	{46, 0, 0},	{46, 0, 0},	{44, 0, 1},
			{42, 0, 1},	{42, 0, 1},	{40, 0, 0},	{40, 0, 0},	{39, 0, 0},
			{38, 0, 0},	{38, 0, 0},	{36, 0, 0},	{35, 0, 0},	{34, 0, 0},
			{34, 0, 0},	{32, 0, 1},	{30, 0, 0},	{30, 0, 0},	{29, 1, 0},
			{28, 0, 0},	{28, 0, 0},	{26, 0, 1},	{24, 0, 0},	{22, 1, 0},
			{22, 1, 0},	{21, 1, 0},	{20, 0, 0},	{19, 1, 1},	{19, 1, 1},
			{16, 0, 0},	{16, 0, 0},	{16, 0, 0},	{14, 0, 0},	{12, 0, 0},
			{12, 0, 0},	{11, 1, 0},	{10, 0, 0},	{9, 1, 0},	{8, 0, 0},
			{6, 0, 0},	{6, 0, 0},	{5, 1, 0},	{4, 0, 0},	{2, 1, 0},
			{2, 1, 0},	{1, 1, 1},	{0, 0, 0},	{0, 0, 0},	{0, 0, 0},
		};

	
	public void generateNoise(int[] noiseData, int rows, int cols) {

		float[][] whiteNoise = GenerateWhiteNoise(rows, cols);
		float[][] perlinNoise = GeneratePerlinNoise(whiteNoise, 6); // default value is 6
		//float[][] colorData = MapGradient(perlinNoise);
		float[][] colorData = MapGray(perlinNoise);
		int index = 0;
		for(int row = 0; row<rows; row++) {
			for(int col=0; col<cols; col++) {
				index = row * cols + col;
				noiseData[index] = (int)colorData[row][col];
			}
		}
	}
	
	public float[][] GenerateWhiteNoise(int width, int height)
	{
	    Random random = new Random(System.currentTimeMillis()); //Seed to 0 for testing
	    float[][] noise = new float[width][height];
	 
	    for (int i = 0; i < width; i++)
	    {
	        for (int j = 0; j < height; j++)
	        {
	            noise[i][j] = (float)random.nextDouble();
	        }
	    }
	 
	    return noise;
	}
	
	public float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
	{
	   int width = baseNoise.length;
	   int height = baseNoise[0].length;
	 
	   float[][] smoothNoise = new float[width][height];
	 
	   int samplePeriod = 1 << octave; // calculates 2 ^ k
	   float sampleFrequency = 1.0f / samplePeriod;
	 
	   for (int i = 0; i < width; i++)
	   {
	      //calculate the horizontal sampling indices
	      int sample_i0 = (i / samplePeriod) * samplePeriod;
	      int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
	      float horizontal_blend = (i - sample_i0) * sampleFrequency;
	 
	      for (int j = 0; j < height; j++)
	      {
	         //calculate the vertical sampling indices
	         int sample_j0 = (j / samplePeriod) * samplePeriod;
	         int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
	         float vertical_blend = (j - sample_j0) * sampleFrequency;
	 
	         //blend the top two corners
	         float top = Interpolate(baseNoise[sample_i0][sample_j0],
	            baseNoise[sample_i1][sample_j0], horizontal_blend);
	 
	         //blend the bottom two corners
	         float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
	            baseNoise[sample_i1][sample_j1], horizontal_blend);
	 
	         //final blend
	         smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
	      }
	   }
	 
	   return smoothNoise;
	}
	
	public float Interpolate(float x0, float x1, float alpha)
	{
	   return x0 * (1 - alpha) + alpha * x1;
	}
	
	public float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
	{
	   int width = baseNoise.length;
	   int height = baseNoise[0].length;
	 
	   float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
	 
	   float persistance = 0.5f; // default value is 0.5f
	 
	   //generate smooth noise
	   for (int i = 0; i < octaveCount; i++)
	   {
	       smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
	   }
	 
	    float[][] perlinNoise = new float[width][height];
	    float amplitude = 1.0f;
	    float totalAmplitude = 0.0f;
	 
	    //blend noise together
	    for (int octave = octaveCount - 1; octave >= 0; octave--)
	    {
	       amplitude *= persistance;
	       totalAmplitude += amplitude;
	 
	       for (int i = 0; i < width; i++)
	       {
	          for (int j = 0; j < height; j++)
	          {
	             perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
	          }
	       }
	    }
	 
	   //normalization
	   for (int i = 0; i < width; i++)
	   {
	      for (int j = 0; j < height; j++)
	      {
	         perlinNoise[i][j] /= totalAmplitude;
	      }
	   }
	 
	   return perlinNoise;
	}
	
	float[][] MapGray(float[][] perlinNoise)
	{
	   int width = perlinNoise.length;
	   int height = perlinNoise[0].length;
	   float[][] image = new float[width][height];
	   int ta=0, tr=0, tb=0, tg=0;
	   for (int i = 0; i < width; i++)
	   {
	      for (int j = 0; j < height; j++)
	      {
	    	  ta = 255;
	    	  int u = (int)(perlinNoise[i][j] * (float)80.0);
	    	  tr = u+100;
	    	  tg = u+100;
	    	  tb = u+100;
	    	  //ta = (int)(255.0f * perlinNoise[i][j]);
	    	  image[i][j] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
	    	  
	      }
	   }
	   
	   return image;
	}
	
	float[][] MapGradient(float[][] perlinNoise)
	{
	   int width = perlinNoise.length;
	   int height = perlinNoise[0].length;
	   float[][] image = new float[width][height];
	   int ta=0, tr=0, tb=0, tg=0;
	   for (int i = 0; i < width; i++)
	   {
	      for (int j = 0; j < height; j++)
	      {
	    	  ta = 255;
	    	  int u = (int)(perlinNoise[i][j] * (float)angryFireColorTable.length);
	    	  tr = (int)angryFireColorTable[u][0];
	    	  tg = (int)angryFireColorTable[u][1];
	    	  tb = (int)angryFireColorTable[u][2];
	    	  image[i][j] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
	      }
	   }
	   
	   return image;
	}

}


  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
OpenGL柏林噪声算法是一种用于生成逼真的随机纹理和模拟自然材质的算法。它是由科学家肯·普莱伯(Ken Perlin)在1983年创造并命名的。 柏林噪声算法的基本原理是通过在一个网格中的各个顶点上分配随机数值,然后使用插值方法在网格内的其他点上计算出合适的数值。它的优点是生成的噪声表现出自然界中的一些特点,如渐变、连续性和纹理的自相似性。 算法的核心是一个被称为“Perlin 网格”的数据结构。这个网格是一个三维的基于向量的数组,每个向量代表一个网格点。在网格的每个顶点上,使用一个随机化的梯度向量来分配一个随机数值。然后通过在网格中插值计算,每个网格内的其他点都可以根据周围顶点的值来计算出一个合适的数值。 柏林噪声算法被广泛应用于计算机图形学的领域,特别是在生成逼真的纹理和模拟自然材质方面。它能够为平滑的、有机的形状创建真实感纹理,并且常用于实现地形生成、纹理合成、云雾效果、水波动效果等。 通过改变柏林噪声算法中的参数,例如插值方法、频率、幅度和持续时间等,可以创建出不同类型的噪声效果。这使得开发人员可以根据需求,灵活地控制生成的纹理和模拟效果的外观和表现。 总之,OpenGL柏林噪声算法是一种优秀的随机纹理生成算法,它为计算机图形学带来了丰富的纹理和模拟效果,并在游戏开发、电影特效等领域中得到广泛应用。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gloomyfish

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值