OpenGL环境下PLY三维模型的读入与显示

相关链接

OpenGL环境下PLY三维模型的读入与显示的源码-CSDN下载

codeblocks配置OpenGL环境-CSDN博客

问题描述

仿照PLY格式点云读取代码,实现PLY格式三角形面片模型的读取与显示。

问题分析

分析ply文件结构
ply
format ascii 1.0 {ascii/二进制,格式版本数}
comment VCGLIB generated {注释关键词说明}
element vertex 34835 {定义“vertex”(顶点)元素,在文件中有34835个}
property float x {顶点包含浮点坐标“x”,下同}
property float y
property float z
property float nx
property float ny
property float nz
element face 69666 {在文件里有69666个“face”(面片)}
property list uchar int vertex_indices
end_header {划定头部结尾}
0.006242 0.039472 0.033507 -1.34547 -4.89656 -1.80904 {顶点列表的开始}
0.007691 0.039029 0.032395 -2.35354 -5.66907 -0.381422
0.008423 0.038501 0.033018 -3.33184 -4.82115 -1.7236

3 0 1 2   {面片列表开始}
3 3 4 5
3 6 7 8
3 8 9 10

编写程序

1)获取到ply文件中顶点的个数和面片的个数

fscanf(fp, "element vertex %d\n", &g_nVerticesNumber);

j = 0;

while (strcmp(type,"element")!=0)

{

    fgets(strBuff, 256, fp);

    i = 0;

    while(strBuff[i]!='\0'&&strBuff[i]!=' '&&strBuff[i]!='\n')

    {

        type[i] = strBuff[i];

        i++;

    }

    if (bStartPropertyCount&&strcmp(type,"property")==0)

        j++;

    else

        bStartPropertyCount = false;

    type[i] = '\0';

}

sscanf(strBuff, "element face %d\n", &g_nFacesNumber);

2)接下来逐行读取点坐标和法向量,这里的J为6

//Read in the triangles

for (i=0; i<g_nVerticesNumber; i++)

{

    if (j==3)

        fscanf(fp, "%f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z);

    else if(j==6)

   fscanf(fp, "%f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z, &g_norm[i].x, &g_norm[i].y, &g_norm[i].z);

    else if (j==7)

    fscanf(fp, "%f %f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z, &g_color[i].x, &g_color[i].y, &g_color[i].z, &alpha);

    else if (j==10)

        fscanf(fp, "%f %f %f %f %f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z,

                                                                    &g_norm[i].x, &g_norm[i].y, &g_norm[i].z,

                                                                   &g_color[i].x, &g_color[i].y, &g_color[i].z, &alpha);

    else

    {

        printf("Warning: the viewer cann't read the colors of models\n");

        exit(1);

    }

    GetMinMax(vMin, vMax, g_vet[i]);

    Normalize(g_norm[i]);

}

3)将法向量标准化

void Normalize(VERTEX3D &v)

{

    double length = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);

    if(length!=0)

    {

        v.x = v.x/length;

        v.y = v.y/length;

        v.z = v.z/length;

    }

}

4)通过三角形面片绘制斯坦福兔子三维模型

void DrawVertices()
{
    int i;
    glPointSize(2.0);
    glBegin(GL_TRIANGLES);
        for(i=0;i<g_nFacesNumber;i++)
        {
            glBegin(GL_TRIANGLES);

            glNormal3f(g_norm[triPatch[i].A].x,g_norm[triPatch[i].A].y,g_norm[triPatch[i].A].z);
            glNormal3f(g_norm[triPatch[i].B].x,g_norm[triPatch[i].B].y,g_norm[triPatch[i].B].z);
            glNormal3f(g_norm[triPatch[i].C].x,g_norm[triPatch[i].C].y,g_norm[triPatch[i].C].z);

            glColor3f(g_color[triPatch[i].A].x,g_color[triPatch[i].A].y,g_color[triPatch[i].A].z);
            glColor3f(g_color[triPatch[i].B].x,g_color[triPatch[i].B].y,g_color[triPatch[i].B].z);
            glColor3f(g_color[triPatch[i].C].x,g_color[triPatch[i].C].y,g_color[triPatch[i].C].z);
            glVertex3f(g_vet[triPatch[i].A].x,g_vet[triPatch[i].A].y,g_vet[triPatch[i].A].z);
            glVertex3f(g_vet[triPatch[i].B].x,g_vet[triPatch[i].B].y,g_vet[triPatch[i].B].z);
            glVertex3f(g_vet[triPatch[i].C].x,g_vet[triPatch[i].C].y,g_vet[triPatch[i].C].z);
            glEnd();
        }
}

运行截图

参考代码

#include <GL/glut.h>
#include <cstdio>
#include <cmath>
//声明结构体用于处理三维点云数据
typedef struct VERTEX_3D
{
    float x;
    float y;
    float z;
} VERTEX3D;

//三角
typedef struct _TRIANGLE
{
    int A;//Index of vertex 0
    int B;//Index of vertex 1
    int C;//Index of vertex 2
} TRIANGLE;

VERTEX3D *g_vet;
VERTEX3D *g_norm;
VERTEX3D *g_color;


TRIANGLE *triPatch;

float g_rotx = 20.0f, g_roty = 30.0f, g_rotz = 0.0f;
float g_modelPos[3] = {0, 0, -15.0f};
float g_scale = 10.0f;

const float g_lightPos[] = { 1.0f, 1.0f, 1.0f, 0.0f };

char g_strModelName[512] = "plant.ply"; /*Problem happens when left the [512] being empty []*/
int g_nVerticesNumber;//顶点数
int g_nFacesNumber;//面数

//归一化
void Normalize(VERTEX3D &v)
{
    double length = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);

    if(length!=0)
    {
        v.x = v.x/length;
        v.y = v.y/length;
        v.z = v.z/length;
    }
}

//获取最小最大值
void GetMinMax(VERTEX3D &vMin, VERTEX3D &vMax, VERTEX3D v)
{
        if(v.x < vMin.x ) vMin.x = v.x;
        if(v.y < vMin.y ) vMin.y = v.y;
        if(v.z < vMin.z ) vMin.z = v.z;
        if(v.x > vMax.x ) vMax.x = v.x;
        if(v.y > vMax.y ) vMax.y = v.y;
        if(v.z > vMax.z ) vMax.z = v.z;
}

//格式化几何图形
void FormatGeometry(VERTEX3D &v, VERTEX3D vMin, VERTEX3D vMax, float scale)
{
    v.x = scale*(v.x - 0.5*(vMin.x + vMax.x));
    v.y = scale*(v.y - 0.5*(vMin.y + vMax.y));
    v.z = scale*(v.z - 0.5*(vMin.z + vMax.z));
}

//加载网格层
void LoadMeshPLY(char *FileName)
{
    int i, j;
    VERTEX3D vMin, vMax;
    char strBuff[512];
    char type[512] = "";
    bool bStartPropertyCount = true;//开始属性计数
    float alpha;

    vMin.x = vMin.y = vMin.z = 99999.0;
    vMax.x = vMax.y = vMax.z = -99999.0;

    FILE* fp=fopen(FileName,"r");

    if (fp==NULL)//文件不存在
    {
        printf("ERROR: unable to open model [%s]!\n",FileName);
        exit(0);
    }
    //读文件
    fgets(strBuff, 256, fp);
    fgets(strBuff, 256, fp);
    fgets(strBuff, 256, fp);
    fscanf(fp, "element vertex %d\n", &g_nVerticesNumber);
    j = 0;
    while (strcmp(type,"element")!=0)
    {
        fgets(strBuff, 256, fp);
        i = 0;
        while(strBuff[i]!='\0'&&strBuff[i]!=' '&&strBuff[i]!='\n')
        {
            type[i] = strBuff[i];
            i++;
        }
        if (bStartPropertyCount&&strcmp(type,"property")==0)
            j++;
        else
            bStartPropertyCount = false;
        type[i] = '\0';
    }
    sscanf(strBuff, "element face %d\n", &g_nFacesNumber);
    printf("Number of Triangles: %d\n", g_nFacesNumber);

    fgets(strBuff, 256, fp);
    fgets(strBuff, 256, fp);
    //fgets(strBuff, 256, fp);多读的一点

    //Allocate the triangle array
    g_vet = new VERTEX3D[g_nVerticesNumber];
    g_norm = new VERTEX3D[g_nVerticesNumber];
    g_color = new VERTEX3D[g_nVerticesNumber];

    triPatch = new TRIANGLE[g_nFacesNumber];

    //Read in the triangles
    for (i=0; i<g_nVerticesNumber; i++)
    {
        if (j==3)
            fscanf(fp, "%f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z);
        else if(j==6)
            fscanf(fp, "%f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z, &g_norm[i].x, &g_norm[i].y, &g_norm[i].z);
        else if (j==7)
            fscanf(fp, "%f %f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z, &g_color[i].x, &g_color[i].y, &g_color[i].z, &alpha);
        else if (j==10)
            fscanf(fp, "%f %f %f %f %f %f %f %f %f %f", &g_vet[i].x, &g_vet[i].y, &g_vet[i].z,
                                                                        &g_norm[i].x, &g_norm[i].y, &g_norm[i].z,
                                                                       &g_color[i].x, &g_color[i].y, &g_color[i].z, &alpha);
        else
        {
            printf("Warning: the viewer cann't read the colors of models\n");
            exit(1);
        }
        GetMinMax(vMin, vMax, g_vet[i]);
        Normalize(g_norm[i]);
    }

    int tmp;
    for (i=0; i<g_nFacesNumber; i++)
    {
        fscanf(fp, "%d %d %d %d",&tmp , &triPatch[i].A, &triPatch[i].B, &triPatch[i].C);
    }

    if((vMax.x - vMin.x) > (vMax.y-vMin.y))
    {
        if ((vMax.x - vMin.x) > (vMax.z - vMin.z))
            g_scale = g_scale/(vMax.x - vMin.x);
        else
            g_scale = g_scale/(vMax.z - vMin.z);
    }
    else
    {
        if ((vMax.y - vMin.y) > (vMax.z - vMin.z))
            g_scale = g_scale/(vMax.y - vMin.y);
        else
            g_scale = g_scale/(vMax.z - vMin.z);
    }

    for (i=0; i<g_nVerticesNumber; i++)
        FormatGeometry(g_vet[i], vMin, vMax, g_scale);

    fclose(fp);
}

//绘图顶点
void DrawVertices()
{
    int i;

    //glEnable(GL_LIGHTING);
    glPointSize(2.0);
    glBegin(GL_TRIANGLES);
        for(i=0;i<g_nFacesNumber;i++)
        {
            glBegin(GL_TRIANGLES);
//glBegin();创建元素的类型,比如:点,线等。
//GL_POINTS:把每个顶点作为一个点进行处理,顶点n定义了点n,绘制N个点。
//GL_LINES:   把每个顶点作为一个独立的线段,顶点2n-1和2n之间定义了n条线段,绘制N/2条线段
//GL_LINE_STRIP:绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个顶点定义了线段n,绘制n-1条线段。
//GL_LINE_LOOP: 绘制从第一个顶点到最后一个顶点依次相连的一组线段,然后最后一个顶点和第一个顶点相连,第n和n+1个顶点定义了线段n,绘制n条线段。
//GL_TRIANGLES: 把每个顶点作为一个独立的三角形,顶点3n-2,3n-1和3n定义了第n个三角形,绘制了N/3个三角形。
//GL_TRIANGLE_STPIP:绘制一组相连的三角形,对于奇数n,顶点n,n+1,和n+2定义了第n个三角形;对于偶数n,顶点n+1,n和n+2定义了第n个三角形,绘制N-2个三角 形。
//GL_QUADS: 绘制由四个顶点组成的一组单独的四边形。顶点4n-3,4n-2,4n-1和4n定义了第n个四边形。绘制了N/4个四边形。
//GL_QUAD_STRIP:绘制一组相连的四边形。每个四边形是由一对顶点及其后给定的一对顶点共同确定的。顶点2n-1,2n,2n+2和2n+1定义了第n个四边形,绘制了N/2-1个   四边形。
//GL_POLYGON:      绘制了一个凸多边形。顶点1到n定义了这个多边形。



            glNormal3f(g_norm[triPatch[i].A].x,g_norm[triPatch[i].A].y,g_norm[triPatch[i].A].z);
            glNormal3f(g_norm[triPatch[i].B].x,g_norm[triPatch[i].B].y,g_norm[triPatch[i].B].z);
            glNormal3f(g_norm[triPatch[i].C].x,g_norm[triPatch[i].C].y,g_norm[triPatch[i].C].z);

            glColor3f(g_color[triPatch[i].A].x,g_color[triPatch[i].A].y,g_color[triPatch[i].A].z);
            glColor3f(g_color[triPatch[i].B].x,g_color[triPatch[i].B].y,g_color[triPatch[i].B].z);
            glColor3f(g_color[triPatch[i].C].x,g_color[triPatch[i].C].y,g_color[triPatch[i].C].z);
            glVertex3f(g_vet[triPatch[i].A].x,g_vet[triPatch[i].A].y,g_vet[triPatch[i].A].z);
            glVertex3f(g_vet[triPatch[i].B].x,g_vet[triPatch[i].B].y,g_vet[triPatch[i].B].z);
            glVertex3f(g_vet[triPatch[i].C].x,g_vet[triPatch[i].C].y,g_vet[triPatch[i].C].z);


            glEnd();
        }
        //glEnd();
}


static void resize(int width, int height)
{
    const float ar = (float) width / (float) height;

    glViewport(0, 0, width, height);//0,0指定了窗口的左下角位置。
                                    //width,height表示视口矩形的宽度和高度,根据窗口的实时变化重绘窗口。
    glMatrixMode(GL_PROJECTION);//将当前矩阵指定为投影矩阵
    glLoadIdentity();//把矩阵设为单位矩阵
    gluPerspective(45, ar, 1.0, 100.0);//指定要观察的视景体在世界坐标系中的具体大小
    glMatrixMode(GL_MODELVIEW);//指定当前要操作的矩阵, GL_MODELVIEW模型视图
    glLoadIdentity() ;
}

static void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glLoadIdentity() ;

    glTranslatef(g_modelPos[0], g_modelPos[1], g_modelPos[2]);
    //定义一个平移矩阵,该矩阵与当前矩阵相乘,使后续的图形相对与当前所在的屏幕位置。进行平移变换。

    glRotatef(g_rotx, 1.0, 0.0, 0.0);
    glRotatef(g_roty, 0.0, 1.0, 0.0);
    glRotatef(g_rotz, 0.0, 0.0, 1.0);

    DrawVertices();

    glutSwapBuffers();
}


static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 27 ://ASCLL表'ESC'
        exit(0);
        break;
    case 'w':
        g_rotx += 5;
        break;
    case 's':
        g_rotx -= 5;
        break;
    case 'a':
        g_roty += 5;
        break;
    case 'd':
        g_roty -= 5;
        break;
    case 'q':
        g_rotz += 5;
        break;
    case 'e':
        g_rotz -= 5;
        break;
    case 'z':
        g_modelPos[2] += 1;
        break;
    case 'x':
        g_modelPos[2] -= 1;
        break;
    }

    glutPostRedisplay();//重绘图像,否则图像只有响应鼠标或键盘消息时才会更新图像。
}

static void idle(void)
{
    glutPostRedisplay();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);//初始化GLUT
    glutInitWindowSize(640,480);//定义窗口大小
    glutInitWindowPosition(10,10);//定义窗口位置
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

    printf("Tips: use 'w', 's', 'a', 'd', 'z', 'x', 'q', 'e' keys to control the model\n");
    if(argc<2)
    {
        printf("Input the file name (without postfix e.g. .ply) of points:");
        scanf("%s", g_strModelName);
        strcat(g_strModelName, ".ply");
    }
    else
    {
        printf("%s\n", argv[1]);
        strcpy(g_strModelName, argv[1]);
    }

    glutCreateWindow("PLY Model Viewer");//创建窗口("窗口标题")

    glutReshapeFunc(resize);//自适应屏幕窗口大小的改变
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);

    glClearColor(0.2, 0.55,1.0,1);//设置好清除颜色
    glDisable(GL_CULL_FACE);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);

    glShadeModel(GL_SMOOTH);

    printf("Loading \"%s\"...\n", g_strModelName);
    LoadMeshPLY(g_strModelName);

    glutMainLoop();//使程序进入事件处理循环。该函数必须是main主函数的最后一条语句。

    return EXIT_SUCCESS;
}

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

行秋

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

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

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

打赏作者

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

抵扣说明:

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

余额充值