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

一、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].可见,它和世界坐标系的基是一样的。
这里写图片描述
现在我们来计算如何将它旋转到面对着摄影机。如图2,红色的为我们的BillBoard。有圆圈表示的是我们的Camera,虚线为Camera的三个方向(均为单位化向量)。假设我们的变换矩阵为M。因为,在这里我们只计算Billboard旋转矩阵。则在BillBoard坐标系中的点P1 = [1,0,0] ,P2= [0,1,0], P3=[0,0,1].经过M变换后在世界空间的坐标为:
这里写图片描述

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);

以上代码实现了一个物体在固定地点始终面朝摄像机的效果。
这里写图片描述
这里写图片描述

二、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 ;
这里写图片描述
GLfloat x = (i * 1.0f + 90.0f) / 200.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 200.0f ;
这里写图片描述
GLfloat x = (i * 1.0f + 90.0f) / 20.0f ;
GLfloat y = (j * 1.0f + 90.0f) / 20.0f ;
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值