openGL之glsl入门7下--添加鱼眼及全景球面纹理

    接着上一篇讲,前面已经展示了纹理把图片贴到立方体上,立方体表面是平面的,纹理坐标不需要设计,下面的两个例子都是基于球面贴图的,把鱼眼和全景图片贴到球面上,即实现曲面贴图。实际上,曲面贴图和平面贴图使用的纹理方面的知识没变化,主要难点在于纹理坐标设计,纹理坐标设计涉及到变换模型设计,需要一定的数学基础,下面例子中的两种贴图方法是比较简单的,网上的资料也非常多,学习本章例子的话,最好能推导例子中用的公式(都是简单的三角函数应用)。

1. 鱼眼球面贴图

    一般的鱼眼图是一张正方形图(长方形也无所谓),有效的鱼眼图像都是中心的圆部分,四角部分都是无效的像素区域(一般是黑的),这种鱼眼图片映射到半球面上,即把圆面映射到半球面上,使用这种模型,可以做鱼眼矫正(效果并非最好,实现方便),鱼眼图片一般分前向(对应摄像机壁装)及俯视、仰视(对应摄像机顶装与底装)等几种拍摄方式,下面的例子采用的是仰视方式贴图,另外两种场景算法原理是一致的,会推导一种就能掌握这种贴图方法。

     圆映射到半球面,推导方法如下:

1. 假定半球面圆心在原点,半球面在z轴正方形,圆面在xy平面上(z=0),圆半径R=0.5(直径为1.0,方便对应纹理坐标范围)

2. 半球面上每个点(xyz)映射到圆面的坐标为(xy0),也就是把z坐标直接丢掉,即圆面是半球面在xy平面(z=0)上的投影。

3. 所以z坐标可以由xy坐标表示出来,圆面的(xy )确定了,半球面的(xyz)的坐标也就确定了,反之亦然。

4. 圆面坐标(xy)与纹理坐标的映射就很简单了,分别加上0.5即可,即把范围有-0.5~0.5映射到0~1.0之间。

    绘制的时候,半球面的纹理坐标直接根据顶点坐标计算即可,不需要单独传纹理坐标。

    下面的例子使用一张鱼眼图片上下对称映射成一个球体(沿用上篇球的画法),如想只画半球,顶点坐标设计成半球即可,这是从网上找的一张全景图,如下(使用以下源码,需把图片转成bmp格式的)

 openGL之glsl入门7下--添加鱼眼及全景球面纹理_第1张图片

源码如下:

#include 
#include 
#include 
#include 
#include 
/* 该头文件请到第6章下载*/
#include "vmath.h"
using namespace vmath;
#pragma pack(2)
#define SPLIT_NUM       10
#define POINT_CNT       (2*SPLIT_NUM + 1)       /* 每条边对应的点数*/
#define QUAD_CNT        (2*SPLIT_NUM)           /* 每条边对应的正方形个数*/
#define FISHEYE_IMG     "fisheye.bmp"
typedef unsigned char U8;
typedef unsigned short int U16;
typedef unsigned long int U32;
typedef struct
{
        U16 bfType;         /* windows下该值必需是x4D42,即字符'BM'*/
        U32 bfSize;         /* bmp文件大小,包含bmp文件头,信息头,调色板大小,数据大小*/
        U16 bfReserved1;    /* 保留,必须设置为*/
        U16 bfReserved2;    /* 保留,必须设置为*/
        U32 bfOffBits;      /* rgb数据相对文件头偏移量*/
} BitmapFileHeader;
typedef struct
{
        U32 biSize;         /* 信息头大小sizeof(BitmapInfoHeader) */
        U32 biWidth;        /* 图象的宽度,以象素为单位*/
        U32 biHeight;       /* 图象的高度,以象素为单位,正值倒向位图,负值正向位图*/
        U16 biPlanes;       /* 位面数,设为*/
        U16 biBitCount;     /* 位图位数,可以为,24,16,8,4,1 */
        U32 biCompression;  /* 说明图象数据压缩的类型,BI_RGB(0) BI_BITFIELDS(3)等*/
        U32 biSizeImage;    /* 图像数据大小,包括位图信息大小和数据大小*/
        U32 biXPelsPerMeter;/* 水平分辨率,一般可填*/
        U32 biYPelsPerMeter;/* 垂直分辨率,一般可填*/
        U32 biClrUsed;      /* 颜色索引使用数,一般填,表示都使用*/
        U32 biClrImportant; /* 重要颜色索引数,一般填,表示都重要*/
} BitmapInfoHeader;
typedef struct
{
        BitmapFileHeader bfHeader;
        BitmapInfoHeader biHeader;
} BitmapInfo;
typedef struct
{
        int width;
        int height;
        char * data;
} BitmapLoad;
typedef struct
{
        GLfloat x;
        GLfloat y;
        GLfloat z;
} POINT_S;
static const GLchar * vertex_source =
"#version 330 core\n"
"uniform mat4 pos_matrix;\n"
"uniform mat4 face_matrix;\n"
"layout (location = 0) in vec3 in_position;\n"
"out vec2 tex_coord;\n"
"void main(void)\n"
"{\n"
"       float dist = 2 * sqrt(pow(in_position.x,2)+pow(in_position.y,2)+pow(in_position.z,2));\n"
"   vec4 vpos = vec4(in_position.x/dist,in_position.y/dist,in_position.z/dist,1.0);\n"
"   vec4 tpos = face_matrix * vpos;\n"
"   tex_coord = vec2(tpos.x+0.5,tpos.y+0.5);\n"
"       gl_Position = pos_matrix * face_matrix * vpos;\n"   
"}\n";
static const GLchar * frag_source =
"#version 330 core\n"
"in vec2 tex_coord;\n"
"out vec4 color;\n"
"uniform sampler2D tex;\n"
"void main(void)\n"
"{\n"
"       color = texture(tex, tex_coord);\n"
"}\n";
void loadShader(GLuint program, GLuint type, const GLchar * source)
{
        GLint status = 0;
        const GLchar * shaderSource[] = {source};
        GLuint shader = glCreateShader(type);
        glShaderSource(shader, 1, shaderSource, 0);
        glCompileShader(shader);
        glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
        glAttachShader(program, shader);
}
BitmapLoad * LoadBmp(const char * filename, BitmapLoad * texture_image)
{
        int pos = 0, dataSize = 0;
        FILE * fp = NULL;
        BitmapInfo bmpInfo;
        fp = fopen(filename, "rb");
        if (fp == NULL)
        {
                perror("fopen");
                return NULL;
        }
        memset(&bmpInfo, 0, sizeof(BitmapInfo));
        fread(&bmpInfo, 1, sizeof(BitmapInfo), fp);
        printf("width:%lu height:%lu dataSize:%lu\n", bmpInfo.biHeader.biWidth, bmpInfo.biHeader.biHeight, bmpInfo.biHeader.biSizeImage);
        if (bmpInfo.biHeader.biSizeImage != bmpInfo.biHeader.biWidth * bmpInfo.biHeader.biHeight * 3)
        {
                printf("biSizeImage size error! header:%d bmp size:%d\n", sizeof(BitmapInfo), bmpInfo.bfHeader.bfSize);
                bmpInfo.biHeader.biSizeImage = bmpInfo.biHeader.biWidth * bmpInfo.biHeader.biHeight * 3;
        }
        dataSize = bmpInfo.biHeader.biSizeImage;
        texture_image->data = (char *)malloc(dataSize);
        if (texture_image->data == NULL)
        {
                fclose(fp);
                return NULL;
        }
        fread(texture_image->data, 1, dataSize, fp);
        texture_image->width = bmpInfo.biHeader.biWidth;
        texture_image->height = bmpInfo.biHeader.biHeight;
        pos = ftell(fp);
        printf("bmp pos:%d\n", pos);
        fclose(fp);
        return texture_image;
}
GLuint vao, vbo, ebo,texture;
mat4 pos_matrix,face_matrix;
GLuint pos_matrix_idx,face_matrix_idx;
void display(void)
{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glBindBuffer(GL_ARRAY_BUFFER, vao);
        glUniformMatrix4fv(pos_matrix_idx, 1, GL_FALSE, pos_matrix);
        face_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(90.0f, 1.0f, 0.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(-90.0f, 1.0f, 0.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(90.0f, 0.0f, 1.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(-90.0f, 0.0f, 1.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(180.0f, 1.0f, 0.0f, 0.0f);
        glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
        glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        glutSwapBuffers();
}
/* 只分配一个面的顶点数据,其他面通过旋转绘制*/
POINT_S vertex_list[POINT_CNT][POINT_CNT];
/* 绘制索引,这里使用点的形式划线,注意空间分配*/
GLuint index_list[QUAD_CNT][QUAD_CNT][4];
void init(void)
{
        int i = 0, j = 0, n = 0, cnt = 0;
        POINT_S * p = NULL;
        BitmapLoad textureImage = {0};
        memset(vertex_list, 0, sizeof(vertex_list));
        memset(index_list, 0, sizeof(index_list));
        /* 添加立方体上表面坐标信息*/
        for (i = 0; i < POINT_CNT ; i++)
        {
                for (j = 0; j < POINT_CNT; j++)
                {
                        p = &vertex_list[i][j];
                        p->x = (-0.5f + j * 0.5f / SPLIT_NUM)*128;
                        p->y = (0.5f - i * 0.5f / SPLIT_NUM)*128;
                        p->z = 0.5f * 128;
                }
        }
        /* 添加索引信息,这里按四边形绘制*/
        for (i = 0; i < QUAD_CNT; i++)
        {
                for (j = 0; j < QUAD_CNT; j++)
                {
                        index_list[i][j][0] = POINT_CNT * i + j;
                        index_list[i][j][1] = POINT_CNT * i + j + 1;
                        index_list[i][j][2] = POINT_CNT * (i + 1) + j + 1;
                        index_list[i][j][3] = POINT_CNT * (i + 1) + j;
                }
        }
        GLuint program = glCreateProgram();
        loadShader(program, GL_VERTEX_SHADER, vertex_source);
        loadShader(program, GL_FRAGMENT_SHADER, frag_source);
        glLinkProgram(program);
        glUseProgram(program);
        glGenBuffers(1, &vao);
        glBindBuffer(GL_ARRAY_BUFFER, vao);
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_list), vertex_list, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
        glEnableVertexAttribArray(0);
        glGenBuffers(1, &ebo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_list), index_list, GL_STATIC_DRAW);
        glGenTextures(1, &texture);
        if (LoadBmp(FISHEYE_IMG, &textureImage) != NULL)
        {
                glBindTexture(GL_TEXTURE_2D, texture);
                glTexImage2D(GL_TEXTURE_2D, 0, 3, textureImage.width, textureImage.height, 0, GL_BGR, GL_UNSIGNED_BYTE, textureImage.data);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//GL_LINEAR  GL_NEAREST
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
                free(textureImage.data);
        }
        /* 定义旋转数组,主要存放当前朝向和旋转信息*/
        face_matrix_idx = glGetUniformLocation(program, "face_matrix");
        pos_matrix_idx = glGetUniformLocation(program, "pos_matrix");
        pos_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
        glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
        glClearDepth(1.0);
        glEnable(GL_DEPTH_TEST);
}
void keyboard(unsigned char key, int x, int y)
{
        switch (key)
        {
        case '-':
                pos_matrix *= vmath::scale(0.95f);
                break;
        case '=':
        case '+':
                pos_matrix *= vmath::scale(1.05f);
                break;
        case 'i':
                pos_matrix = vmath::frustum(-0.5, 0.5, -0.5, 0.5, 0.4, 10.0);
                break;
        case 'o':
                pos_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
                break;
        default:
                break;
        }
        glutPostRedisplay();
}
void specialKey(GLint key, GLint x, GLint y)
{
        float step = 2.0f;
        switch (key)
        {
        case GLUT_KEY_UP:
                pos_matrix *= vmath::rotate(step, 1.0f, 0.0f, 0.0f);
                break;
        case GLUT_KEY_DOWN:
                pos_matrix *= vmath::rotate(-1.0f * step, 1.0f, 0.0f, 0.0f);
                break;
        case GLUT_KEY_LEFT:
                pos_matrix *= vmath::rotate(step, 0.0f, 1.0f, 0.0f);
                break;
        case GLUT_KEY_RIGHT:
                pos_matrix *= vmath::rotate(-1.0f * step, 0.0f, 1.0f, 0.0f);
                break;
        case GLUT_KEY_PAGE_UP          :
                pos_matrix *= vmath::rotate(step, 0.0f, 0.0f, 1.0f);
                break;
        case GLUT_KEY_PAGE_DOWN:
                pos_matrix *= vmath::rotate(-1.0f * step, 0.0f, 0.0f, 1.0f);
                break;
        default:
                break;
        }
        glutPostRedisplay();
}
int main(int argc, char * argv[])
{
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
        glutInitWindowPosition(200, 200);
        glutInitWindowSize(400, 400);
        glutCreateWindow("FishEye");
        glewInit();
        init();
        glutDisplayFunc(display);
        glutKeyboardFunc(keyboard);
        glutSpecialFunc(specialKey);
        glutMainLoop();
        return 0;
}

    效果如下:

 openGL之glsl入门7下--添加鱼眼及全景球面纹理_第2张图片

2. 全景球面贴图

    全景图和鱼眼图不一样,可以看作是球面映射的两个相反的方面,鱼眼是半球面投影,而全景图是曲面展开,所以鱼眼图像是可以转成全景图的。这里从网上找了一张全景图(世界地图),宽高比例是21,为什么是21呢?原因是:

1. 全景图是按球面展开,球的圆周(赤道)展开为宽,经线展开为高。

2. 圆周长度为2πR,经线长为πR,所以是21

注意:这种是等弧长映射,球面经线方向的点和全景图高度方向的点是一一对应的,而不是简单的认为全景图高度等于球体的高度。

    纹理坐标映射推导过程:

1. 与上例一样,球面半径为0.5,圆心在原点。

2. 画一个外接圆柱体,半径0.5,总高度为πR,即π/2,把全景图映射到圆柱面上。

3. 横坐标的关系是,宽度=水平弧长,最终总宽度=圆周。

4. 纵坐标的关系是,高度=垂直弧长,总高度=半圆周。(所以叫等弧长映射)

5. 有了上面两个关系量,可以得到球面坐标和纹理坐标的关系(利用好等弧长这个条件即可)。

    上面的过程比较难说清楚,如果理解了映射过程(平面->柱面->球面),自己推导比看人家讲解的快(只使用了最基本的三角和反三角函数),先把网上找的图贴一下(使用以下源码,需把图片转成bmp格式的)

 openGL之glsl入门7下--添加鱼眼及全景球面纹理_第3张图片

源码如下:

#include 
#include 
#include 
#include 
#include 
/* 该头文件请到第6章下载*/
#include "vmath.h"
using namespace vmath;
#pragma pack(2)
#define SPLIT_NUM       10
#define POINT_CNT       (2*SPLIT_NUM + 1)       /* 每条边对应的点数*/
#define QUAD_CNT        (2*SPLIT_NUM)           /* 每条边对应的正方形个数*/
#define FISHEYE_IMG     "world2.bmp"
typedef unsigned char U8;
typedef unsigned short int U16;
typedef unsigned long int U32;
typedef struct
{
    U16 bfType;         /* windows下该值必需是x4D42,即字符'BM'*/
    U32 bfSize;         /* bmp文件大小,包含bmp文件头,信息头,调色板大小,数据大小*/
    U16 bfReserved1;    /* 保留,必须设置为*/
    U16 bfReserved2;    /* 保留,必须设置为*/
    U32 bfOffBits;      /* rgb数据相对文件头偏移量*/
} BitmapFileHeader;
typedef struct
{
    U32 biSize;         /* 信息头大小sizeof(BitmapInfoHeader) */
    U32 biWidth;        /* 图象的宽度,以象素为单位*/
    U32 biHeight;       /* 图象的高度,以象素为单位,正值倒向位图,负值正向位图*/
    U16 biPlanes;       /* 位面数,设为*/
    U16 biBitCount;     /* 位图位数,可以为,24,16,8,4,1 */
    U32 biCompression;  /* 说明图象数据压缩的类型,BI_RGB(0) BI_BITFIELDS(3)等*/
    U32 biSizeImage;    /* 图像数据大小,包括位图信息大小和数据大小*/
    U32 biXPelsPerMeter;/* 水平分辨率,一般可填*/
    U32 biYPelsPerMeter;/* 垂直分辨率,一般可填*/
    U32 biClrUsed;      /* 颜色索引使用数,一般填,表示都使用*/
    U32 biClrImportant; /* 重要颜色索引数,一般填,表示都重要*/
} BitmapInfoHeader;
typedef struct
{
    BitmapFileHeader bfHeader;
    BitmapInfoHeader biHeader;
} BitmapInfo;
typedef struct
{
    int width;
    int height;
    char * data;
} BitmapLoad;
typedef struct
{
    GLfloat x;
    GLfloat y;
    GLfloat z;
} POINT_S;
static const GLchar * vertex_source =
    "#version 330 core\n"
    "uniform mat4 pos_matrix;\n"
    "uniform mat4 face_matrix;\n"
    "layout (location = 0) in vec3 in_position;\n"
    "out vec2 tex_coord;\n"
    "void main(void)\n"
    "{\n"
    "   float dist = 2 * sqrt(pow(in_position.x,2)+pow(in_position.y,2)+pow(in_position.z,2));\n"
    "   vec4 vpos = vec4(in_position.x/dist,in_position.y/dist,in_position.z/dist,1.0);\n"
    "   vec4 tpos = face_matrix * vpos;\n"
        "       float theta = atan(tpos.y/tpos.x);\n"
        "       if( tpos.x < 0.0) theta+=3.14; \n"
        "       else if(tpos.y<0.0) theta+=6.28; \n"
        "       float ty = asin(2*tpos.z)/3.14;\n"
        "   tex_coord = vec2(theta/3.14/2,ty * (-1.0)+0.5);\n"
    "   gl_Position = pos_matrix * face_matrix * vpos;\n"   
    "}\n";
static const GLchar * frag_source =
    "#version 330 core\n"
    "in vec2 tex_coord;\n"
    "out vec4 color;\n"
    "uniform sampler2D tex;\n"
    "void main(void)\n"
    "{\n"
        "       color = texture(tex, tex_coord);\n"
    "}\n";
void loadShader(GLuint program, GLuint type, const GLchar * source)
{
    GLint status = 0;
    const GLchar * shaderSource[] = {source};
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, shaderSource, 0);
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    glAttachShader(program, shader);
}
BitmapLoad * LoadBmp(const char * filename, BitmapLoad * texture_image)
{
    int pos = 0, dataSize = 0;
    FILE * fp = NULL;
    BitmapInfo bmpInfo;
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        perror("fopen");
        return NULL;
    }
    memset(&bmpInfo, 0, sizeof(BitmapInfo));
    fread(&bmpInfo, 1, sizeof(BitmapInfo), fp);
    printf("width:%lu height:%lu dataSize:%lu\n", bmpInfo.biHeader.biWidth, bmpInfo.biHeader.biHeight, bmpInfo.biHeader.biSizeImage);
    if (bmpInfo.biHeader.biSizeImage != bmpInfo.biHeader.biWidth * bmpInfo.biHeader.biHeight * 3)
    {
        printf("biSizeImage size error! header:%d bmp size:%d\n", sizeof(BitmapInfo), bmpInfo.bfHeader.bfSize);
                bmpInfo.biHeader.biSizeImage = bmpInfo.biHeader.biWidth * bmpInfo.biHeader.biHeight * 3;
    }
    dataSize = bmpInfo.biHeader.biSizeImage;
    texture_image->data = (char *)malloc(dataSize);
    if (texture_image->data == NULL)
    {
        fclose(fp);
        return NULL;
    }
    fread(texture_image->data, 1, dataSize, fp);
    texture_image->width = bmpInfo.biHeader.biWidth;
    texture_image->height = bmpInfo.biHeader.biHeight;
    pos = ftell(fp);
    printf("bmp pos:%d\n", pos);
    fclose(fp);
    return texture_image;
}
GLuint vao, vbo, ebo,texture;
mat4 pos_matrix,face_matrix;
GLuint pos_matrix_idx,face_matrix_idx;
void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBindBuffer(GL_ARRAY_BUFFER, vao);
    glUniformMatrix4fv(pos_matrix_idx, 1, GL_FALSE, pos_matrix);
        face_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(90.0f, 1.0f, 0.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(-90.0f, 1.0f, 0.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(90.0f, 0.0f, 1.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(-90.0f, 0.0f, 1.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
        face_matrix = vmath::rotate(180.0f, 1.0f, 0.0f, 0.0f);
    glUniformMatrix4fv(face_matrix_idx, 1, GL_FALSE, face_matrix);
    glDrawElements(GL_QUADS, QUAD_CNT * QUAD_CNT * 4, GL_UNSIGNED_INT, (GLvoid *)(0));
    glutSwapBuffers();
}
   /* 只分配一个面的顶点数据,其他面通过旋转绘制*/
    POINT_S vertex_list[POINT_CNT][POINT_CNT];
    /* 绘制索引,这里使用点的形式划线,注意空间分配*/
    GLuint index_list[QUAD_CNT][QUAD_CNT][4];
void init(void)
{
    int i = 0, j = 0, n = 0, cnt = 0;
    POINT_S * p = NULL;
        BitmapLoad textureImage = {0};
    memset(vertex_list, 0, sizeof(vertex_list));
        memset(index_list, 0, sizeof(index_list));
    /* 添加立方体上表面坐标信息*/
    for (i = 0; i < POINT_CNT ; i++)
    {
        for (j = 0; j < POINT_CNT; j++)
        {
            p = &vertex_list[i][j];
            p->x = (-0.5f + j * 0.5f / SPLIT_NUM)*128;
            p->y = (0.5f - i * 0.5f / SPLIT_NUM)*128;
            p->z = 0.5f * 128;
        }
    }
    /* 添加索引信息,这里按四边形绘制*/
    for (i = 0; i < QUAD_CNT; i++)
    {
        for (j = 0; j < QUAD_CNT; j++)
        {
            index_list[i][j][0] = POINT_CNT * i + j;
            index_list[i][j][1] = POINT_CNT * i + j + 1;
            index_list[i][j][2] = POINT_CNT * (i + 1) + j + 1;
            index_list[i][j][3] = POINT_CNT * (i + 1) + j;
        }
    }
    GLuint program = glCreateProgram();
    loadShader(program, GL_VERTEX_SHADER, vertex_source);
    loadShader(program, GL_FRAGMENT_SHADER, frag_source);
    glLinkProgram(program);
    glUseProgram(program);
    glGenBuffers(1, &vao);
    glBindBuffer(GL_ARRAY_BUFFER, vao);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_list), vertex_list, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
    glEnableVertexAttribArray(0);
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_list), index_list, GL_STATIC_DRAW);
        glGenTextures(1, &texture);
    if (LoadBmp(FISHEYE_IMG, &textureImage) != NULL)
    {
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, textureImage.width, textureImage.height, 0, GL_BGR, GL_UNSIGNED_BYTE, textureImage.data);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//GL_LINEAR  GL_NEAREST
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
       free(textureImage.data);
    }
    /* 定义旋转数组,主要存放当前朝向和旋转信息*/
        face_matrix_idx = glGetUniformLocation(program, "face_matrix");
    pos_matrix_idx = glGetUniformLocation(program, "pos_matrix");
    pos_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
    glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
    glClearDepth(1.0);
    glEnable(GL_DEPTH_TEST);
}
void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
        case '-':
            pos_matrix *= vmath::scale(0.95f);
            break;
        case '=':
        case '+':
            pos_matrix *= vmath::scale(1.05f);
            break;
                case 'i':
                         pos_matrix = vmath::frustum(-0.5, 0.5, -0.5, 0.5, 0.4, 10.0);
                        break;
                case 'o':
            pos_matrix = vmath::translate(0.0f, 0.0f, 0.0f);
                        break;
        default:
            break;
    }
    glutPostRedisplay();
}
void specialKey(GLint key, GLint x, GLint y)
{
    float step = 2.0f;
    switch (key)
    {
        case GLUT_KEY_UP:
            pos_matrix *= vmath::rotate(step, 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_DOWN:
            pos_matrix *= vmath::rotate(-1.0f * step, 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_LEFT:
            pos_matrix *= vmath::rotate(step, 0.0f, 1.0f, 0.0f);
            break;
        case GLUT_KEY_RIGHT:
            pos_matrix *= vmath::rotate(-1.0f * step, 0.0f, 1.0f, 0.0f);
            break;
        case GLUT_KEY_PAGE_UP          :
            pos_matrix *= vmath::rotate(step, 0.0f, 0.0f, 1.0f);
            break;
        case GLUT_KEY_PAGE_DOWN:
            pos_matrix *= vmath::rotate(-1.0f * step, 0.0f, 0.0f, 1.0f);
            break;
        default:
            break;
    }
    glutPostRedisplay();
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(400, 400);
    glutCreateWindow("WorldMap");
    glewInit();
    init();
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);
    glutSpecialFunc(specialKey);
    glutMainLoop();
    return 0;
}

    效果如下:

 openGL之glsl入门7下--添加鱼眼及全景球面纹理_第4张图片

说明:上面的贴图方式并不完善,大家可以看到接缝处有一条黑线,原因是顶点模型选择的问题,立方体逼近的方法,在上例鱼眼贴图中效果会好一些,但本例的全景贴图,球面顶点模型用经纬法的更好,否则没办法处理这种黑线问题,黑线产生的原因是,通过顶点坐标计算出的纹理坐标,在接缝线左(靠近1.0)右(靠近0.0)差不多相差1.0,假如有一个四边形跨了接缝,实际跨度很小,只跨了一条缝,但顶点坐标跨度范围会非常大,如0.1~0.9,这个小四边形差不多把整个纹理都填充在里面,所以表现为一条黑线,实际上我们要的效果是0.0~0.10.9~1.0这样的填充,这就必须要求接缝线是直线(意思是经度相同的线,球面上的直线),立方体逼近方法没办法保证接缝线是直线,所以很难去除黑线问题,而经纬法画的球体,刚好有这种特性。大家可以试着把顶点模型改成经纬法的球面,来看效果。 

3. 鱼眼矫正

    鱼眼矫正的算法有很多种(网上有很多论文),上面例子中的映射方法是鱼眼矫正方法的一种,矫正的效果一般,但胜在原理简单,速度快,有产品使用该模型进行实时视频矫正。

    再看一下上面两个例子,是有一些联系的,全景图可以和鱼眼图相互转化,即全景图片<-->柱面映射<-->球面映射<-->鱼眼图片,理解这中间的映射方式,就可以做鱼眼矫正了,鱼眼矫正有转全景、云台、柱面、分割、缩放等多种操作方式,在这种方法中,最关键的就是球面模型建立与处理。鱼眼矫正产品化有一定难度,但原理不复杂,可以用来练手,如把视点定在原点,对球面做投影视图变换,调整角度,就可以实现类似云台的功能。 

4. 关于调试

    shader程序的调试很麻烦,涉及到模型变换时,稍有不对,得出的结果跟预想的完全不一样。shader调试没有很直接的方法,连打印调试都做不到,这正是并行运算的特点,即使可以打印,打印的数据也是乱序,且数据会太多,网上提到的颜色调试、调试器调试不直观也比较麻烦,个人主要调试方法为:

1. 分析与尝试,屏蔽无关顶点,只保留关注的顶点,方便分析,只需要在顶点着色器中把正常的顶点过滤掉即可(判断顶点坐标为不关注顶点时,main函数空函数)。

2. 逻辑部分代码移到CPU上跑,调试OK后,再移到shader里,由于shader语法与C语法类似,移植还是比较方便的,虽然麻烦但有效。

3. 调纹理的时候使用测试图,调好过后再替换成效果图,测试图带定位信息,更容易看清楚顶点和纹理坐标的对应关系。

    下面是调试时使用的测试图(最好看图就能知道对应纹理的位置,格子,环状图都可以),本图做的比较烂,大家可以参考。

 openGL之glsl入门7下--添加鱼眼及全景球面纹理_第5张图片

 

 

 

 

     

 

     

 

你可能感兴趣的:(openGL)