billboard公告板技术与perlin噪声的实现

时间:2021-01-21 12:19:00

一、billboard公告板技术

1.简介

在游戏中经常会遇到使用一张2D贴图来代替3D物体的情况,比如我们可以绘制一个2D的太阳贴图来代替3D的太阳模型,再比如使用2D的灌木、花草贴图来代替其复杂的3D模型,那么如何使2D贴图看起来和3D模型的效果一样呢?这里我们使用billboard公告板技术,使其平面始终面对摄象机就可以了。

2.billboard变换矩阵推导

对于,billboard的推导,网上有很多教程,但是我都没有看懂,然后我找到了一篇论文,所以下面的内容都来自论文——《Billboard的推导》潘李亮2005-1-11。
正文如下:
Billboard为面对着摄影机的一种面片,在绘制BillBoard的时候,首先都假定BillBoard为一个面对着某个方向的简单的四边形。如图1,红色的四边形为我们的BillBoard没有变换前的位置。在本文中,我把所有的BillBoard的朝向都定为+z。即[0,0,1]。因此,我们可以得到我们把图1蓝色的三个方向定义为Billboard局部坐标系的三个正交基,Xb = [1,0,0] , Yb = [0,1,0], Zb = [0,0,1].可见,它和世界坐标系的基是一样的。
billboard公告板技术与perlin噪声的实现
现在我们来计算如何将它旋转到面对着摄影机。如图2,红色的为我们的BillBoard。有圆圈表示的是我们的Camera,虚线为Camera的三个方向(均为单位化向量)。假设我们的变换矩阵为M。因为,在这里我们只计算Billboard旋转矩阵。则在BillBoard坐标系中的点P1 = [1,0,0] ,P2= [0,1,0], P3=[0,0,1].经过M变换后在世界空间的坐标为:
billboard公告板技术与perlin噪声的实现

3.代码实现

mat4 view = camera.GetViewMatrix();
mat4 newview = view;
swap(newview[0][1], newview[1][0]);
swap(newview[0][2], newview[2][0]);
swap(newview[1][2], newview[2][1]);
newview[0][3] = 0.0f;
newview[1][3] = 0.0f;
newview[2][3] = 0.0f;
newview[3][3] = 1.0f;
newview[3][0] = 0.0f;
newview[3][1] = 0.0f;
newview[3][2] = 0.0f;




shader中这样写:
gl_Position = projection * view * model * newview * vec4(position, 1.0f);

以上代码实现了一个物体在固定地点始终面朝摄像机的效果。
billboard公告板技术与perlin噪声的实现
billboard公告板技术与perlin噪声的实现

二、perlin噪声的实现

对于perlin噪声,这里有篇教程讲得非常好,推荐大家去看:http://blog.csdn.net/candycat1992/article/details/50346469

实现代码使用的是http://www.twinklingstar.cn/2015/2581/classical-perlin-noise/这篇教程中的代码。

头文件:

/************************************************************************
\link www.twinklingstar.cn
\author twinklingstar
\file SrClassicalPerlinNoise.h
\date 2015/07/15
****************************************************************************/

#ifndef SR_CLASSIC_PERLIN_NOISE_H_
#define SR_CLASSIC_PERLIN_NOISE_H_

#define SR_PERLIN_N 0x100
#define SR_PERLIN_MASK 0xff
#define SR_PERLIN_PI 3.141592653589793

#include<glm\glm.hpp>

typedef float real;
typedef unsigned char ubyte;


class SrPerlinNoise
{
public:
SrPerlinNoise() {}
protected:
real ease(real x);

int permutate(int x);

real lerp(real t, real value0, real value1);
protected:
static ubyte mP[SR_PERLIN_N];
};


class SrClassicalPerlinNoise1D :public SrPerlinNoise
{
public:
static SrClassicalPerlinNoise1D* create();

real noise(real x);
private:
int index(int ix);

real lattice(int ix, real fx);

static void init();

SrClassicalPerlinNoise1D() {}
SrClassicalPerlinNoise1D(const SrClassicalPerlinNoise1D&);
SrClassicalPerlinNoise1D& operator = (const SrClassicalPerlinNoise1D&);
private:
static real mG[SR_PERLIN_N];
static bool mIsInit;
};

class SrClassicalPerlinNoise2D :public SrPerlinNoise
{
public:
static SrClassicalPerlinNoise2D* create();

real noise_sum_abs(glm::vec2 point);
private:
int index(int ix, int iy);

real lattice(int ix, int iy, real fx, real fy);

static void init();

real noise(real x, real y);

SrClassicalPerlinNoise2D() {}
SrClassicalPerlinNoise2D(const SrClassicalPerlinNoise2D&);
SrClassicalPerlinNoise2D& operator = (const SrClassicalPerlinNoise2D&);
private:
static real mG[SR_PERLIN_N][2];
static bool mIsInit;
};

class SrClassicalPerlinNoise3D :public SrPerlinNoise
{
public:
static SrClassicalPerlinNoise3D* create();

real noise(real x, real y, real z);
;
private:
int index(int ix, int iy, int iz);

real lattice(int ix, int iy, int iz, float fx, float fy, float fz);

static void init();

SrClassicalPerlinNoise3D() {}
SrClassicalPerlinNoise3D(const SrClassicalPerlinNoise3D&);
SrClassicalPerlinNoise3D& operator = (const SrClassicalPerlinNoise3D&);
private:
static real mG[SR_PERLIN_N][3];
static bool mIsInit;
};


#endif

函数实现代码:
这里我对其2D的噪声进行了一些改动,结合第一篇教程中的代码,使其进行了叠加,并做了注释。

/************************************************************************
\link www.twinklingstar.cn
\author twinklingstar
\file SrClassicalPerlinNoise.cpp
\date 2015/07/15
****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "perlin_noise.h"
#include<glm\glm.hpp>

/************************************************************************/
/* class SrPerlinNoise */
/************************************************************************/

real SrPerlinNoise::ease(real x)//3t^2-2t^3,插值函数
{
return (real)(x * x * (3 - 2 * x));
}

int SrPerlinNoise::permutate(int x)//通过按位与找到P表中对应的0~255的值
{
return mP[x & SR_PERLIN_MASK];
}

real SrPerlinNoise::lerp(real t, real value0, real value1)
{
return (real)(value0 + t * (value1 - value0));
}

ubyte SrPerlinNoise::mP[SR_PERLIN_N] = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

/************************************************************************/
/* class SrClassicalPerlinNoise1D */
/************************************************************************/
SrClassicalPerlinNoise1D* SrClassicalPerlinNoise1D::create()
{
if (!mIsInit)
{
mIsInit = true;
init();
}
return (new SrClassicalPerlinNoise1D());
}

real SrClassicalPerlinNoise1D::noise(real x)
{
int x0 = (int)floor(x);
real fx0 = x - x0;
real fx1 = fx0 - 1;

real sx = ease(fx0);

real u = lattice(x0, fx0);
real v = lattice(x0 + 1, fx1);

return lerp(sx, u, v);
}
int SrClassicalPerlinNoise1D::index(int ix)
{
return permutate(ix);
}

real SrClassicalPerlinNoise1D::lattice(int ix, real fx)
{
int indx = index(ix);
return (real)(mG[indx] * fx);
}

void SrClassicalPerlinNoise1D::init()
{
int i;
for (i = 0; i < SR_PERLIN_N; i++)
{
mG[i] = (float)((rand() % (SR_PERLIN_N + SR_PERLIN_N)) - SR_PERLIN_N) / SR_PERLIN_N;
}
}

bool SrClassicalPerlinNoise1D::mIsInit = false;

real SrClassicalPerlinNoise1D::mG[SR_PERLIN_N] = {};

/************************************************************************/
/* class SrClassicalPerlinNoise2D */
/************************************************************************/

SrClassicalPerlinNoise2D* SrClassicalPerlinNoise2D::create()
{
if (!mIsInit)
{
mIsInit = true;
init();
}
return (new SrClassicalPerlinNoise2D());
}

real SrClassicalPerlinNoise2D::noise(real x, real y)
{
int x0 = (int)floor(x);//向下取整获得点所在晶格的左下角坐标
real fx0 = x - x0;//获得小数部分,即点相对于晶格的坐标
real fx1 = fx0 - 1;
real sx = ease(fx0);//获得插值函数

int y0 = (int)floor(y);//同上,算出y轴相关数据
real fy0 = y - y0;
real fy1 = fy0 - 1;
real sy = ease(fy0);

real s = lattice(x0, y0, fx0, fy0);//晶格左下角噪声
real t = lattice(x0 + 1, y0, fx1, fy0);//晶格右下角噪声
real u = lattice(x0, y0 + 1, fx0, fy1);//晶格左上角噪声
real v = lattice(x0 + 1, y0 + 1, fx1, fy1);//晶格右上角噪声

//通过插值得到点所在位置的噪声
real a = lerp(sx, s, t);
real b = lerp(sx, u, v);
return lerp(sy, a, b);
}

int SrClassicalPerlinNoise2D::index(int ix, int iy)
{
return permutate(ix + permutate(iy));
}

real SrClassicalPerlinNoise2D::lattice(int ix, int iy, real fx, real fy)
{
int indx = index(ix, iy);
//通过晶格的四个顶点坐标找到P表中的范围为0~255的随机值
return (real)(mG[indx][0] * fx + mG[indx][1] * fy);
//G表中存储的是随机梯度向量,这里将随机梯度向量与点相对于晶格的向量点乘
}

real SrClassicalPerlinNoise2D::noise_sum_abs(glm::vec2 p)//进行叠加
{
real f = 0.0;
p = p * 7.0f;
f += 1.0000 * abs(noise(p.x, p.y));
p = 2.0f * p;
f += 0.5000 * abs(noise(p.x, p.y));
p = 2.0f * p;
f += 0.2500 * abs(noise(p.x, p.y));
p = 2.0f * p;
f += 0.1250 * abs(noise(p.x, p.y));
p = 2.0f * p;
f += 0.0625 * abs(noise(p.x, p.y));
p = 2.0f * p;

return f;
}


void SrClassicalPerlinNoise2D::init()
{
int i, j;
real s;
for (i = 0; i < SR_PERLIN_N; i++)
{
for (j = 0; j < 2; j++)
mG[i][j] = (float)((rand() % (SR_PERLIN_N + SR_PERLIN_N)) - SR_PERLIN_N) / SR_PERLIN_N;

//向量归一化
s = sqrt((real)(mG[i][0] * mG[i][0] + mG[i][1] * mG[i][1]));
mG[i][0] = mG[i][0] / s;
mG[i][1] = mG[i][1] / s;
}
}

bool SrClassicalPerlinNoise2D::mIsInit = false;

real SrClassicalPerlinNoise2D::mG[SR_PERLIN_N][2] = {};

/************************************************************************/
/* class SrClassicalPerlinNoise3D */
/************************************************************************/

SrClassicalPerlinNoise3D* SrClassicalPerlinNoise3D::create()
{
if (!mIsInit)
{
mIsInit = true;
init();
}
return (new SrClassicalPerlinNoise3D());
}

real SrClassicalPerlinNoise3D::noise(real x, real y, real z)
{
int x0 = (int)floor(x);
real fx0 = x - x0;
real fx1 = fx0 - 1;
real wx = ease(fx0);

int y0 = (int)floor(y);
real fy0 = y - y0;
real fy1 = fy0 - 1;
real wy = ease(fy0);

int z0 = (int)floor(z);
real fz0 = z - z0;
real fz1 = fz0 - 1;
real wz = ease(fz0);

real vx0 = lattice(x0, y0, z0, fx0, fy0, fz0);
real vx1 = lattice(x0 + 1, y0, z0, fx1, fy0, fz0);
real vy0 = lerp(wx, vx0, vx1);

vx0 = lattice(x0, y0 + 1, z0, fx0, fy1, fz0);
vx1 = lattice(x0 + 1, y0 + 1, z0, fx1, fy1, fz0);
real vy1 = lerp(wx, vx0, vx1);

real vz0 = lerp(wy, vy0, vy1);

vx0 = lattice(x0, y0, z0 + 1, fx0, fy0, fz1);
vx1 = lattice(x0 + 1, y0, z0 + 1, fx1, fy0, fz1);
vy0 = lerp(wx, vx0, vx1);

vx0 = lattice(x0, y0 + 1, z0 + 1, fx0, fy1, fz1);
vx1 = lattice(x0 + 1, y0 + 1, z0 + 1, fx1, fy1, fz1);
vy1 = lerp(wx, vx0, vx1);

real vz1 = lerp(wy, vy0, vy1);

return lerp(wz, vz0, vz1);
}

int SrClassicalPerlinNoise3D::index(int ix, int iy, int iz)
{
return permutate(ix + permutate(iy + permutate(iz)));
}

real SrClassicalPerlinNoise3D::lattice(int ix, int iy, int iz, float fx, float fy, float fz)
{
int indx = index(ix, iy, iz);
return (real)(mG[indx][0] * fx + mG[indx][1] * fy + mG[indx][2] * fz);
}

void SrClassicalPerlinNoise3D::init()
{
int i;
for (i = 0; i < SR_PERLIN_N; i++)
{
real z = 1.0f - 2.0f * ((real)::rand() / ((real)RAND_MAX + 1));
real r = (real)sqrt(1.0f - z * z);
real theta = 2 * (real)SR_PERLIN_PI * ((real)::rand() / ((real)RAND_MAX + 1));

mG[i][0] = r * (real)cos(theta);
mG[i][1] = r * (real)sin(theta);
mG[i][2] = z;
}
}


bool SrClassicalPerlinNoise3D::mIsInit = false;

real SrClassicalPerlinNoise3D::mG[SR_PERLIN_N][3] = {};

主函数中的代码:
在这里我制作了一张256*256的纹理贴图来存储噪声。

SrClassicalPerlinNoise2D *perlinNoise;
perlinNoise = perlinNoise->create();
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
{
GLfloat x = (i * 1.0f + 90.0f) / 2000.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 2000.0f ;
noisedata[i][j] = perlinNoise->noise_sum_abs(vec2(x, y));
}
}

GLuint noiseTex;
glGenTextures(1, &noiseTex);
glBindTexture(GL_TEXTURE_2D, noiseTex);
glTexStorage2D(GL_TEXTURE_2D, 9, GL_R8, 256, 256);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RED, GL_FLOAT, noisedata);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

效果:
GLfloat x = (i * 1.0f + 90.0f) / 2000.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 2000.0f ;
billboard公告板技术与perlin噪声的实现
GLfloat x = (i * 1.0f + 90.0f) / 200.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 200.0f ;
billboard公告板技术与perlin噪声的实现
GLfloat x = (i * 1.0f + 90.0f) / 20.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 20.0f ;
billboard公告板技术与perlin噪声的实现