OpenGL通过BMP位图的2D图形生成三维图形

问题描述

根据参考程序,读取BMP位图文件中绘制的2D图片,通过变换,生成有图所示的三维图形。

 问题分析

运行结果

参考代码

#include <GL/freeglut.h>
#include <iostream>
#include <vector>
#include <fstream>
#include <cmath>

#define PI 3.14159265

using namespace std;

float g_rotx = 0.0f, g_roty = 0.0f, g_rotz = 0.0f;
const double g_scale = 10.0;
double g_modelPos[3] = {0, -0.5*g_scale, -15.0f};

float g_originalClr[] = { 0.1, 0.9, 0.1, 1.0 };
float g_selectedClr[] = { 0.9, 0.1, 0.1, 1.0 };
const float g_ambDiff[4] =  {1.0f, 0.5f, 0.0f, 1.0f};
const float g_lightPos[] = { 10.0f, 10.0f, 10.0f, 0.0f };
const float g_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
const float g_shininess[] = { 50.0f };
int g_subdivMax = 0;

char g_strFileName[] = "sketch2.bmp";

struct Vertex3D{
    double x;
    double y;
    double z;
    Vertex3D(double _x=0, double _y=0, double _z=0)
    {
        x = _x;
        y = _y;
        z = _z;
    }
};

struct Circle{
    Vertex3D vt;
    double r;
    Circle()
    {
        r = 0;
    }
};

int g_sideSketchNo = 0;
int g_upSketchNo = 0;
int g_downSketchNo = 0;
const int g_sliceNo = 200;

vector <Vertex3D> g_upVt;
vector <Vertex3D> g_downVt;
vector <Vertex3D> g_sideVt;
Circle g_downCircle, g_upCircle;
vector <Circle> g_arrCircle;

#pragma pack(1)
struct FILEHEADER{
    char dummy1[18];
    int width;
    int height;
    char dummy2[28];
};

struct BGR{
    unsigned char b;
    unsigned char g;
    unsigned char r;
    BGR(unsigned char _b=0, unsigned char _g=0, unsigned char _r=0){
        b = _b;
        g = _g;
        r = _r;
    }
    bool operator==(BGR x){
        if (b==x.b && g==x.g && r==x.r)
            return true;
        else
            return false;
    }
};

void LinearFitting(const vector <Vertex3D> &vt, double &a, double &b){
    double xsum=0, x2sum=0, ysum=0, xysum=0;//variables for sums/sigma of xi,yi,xi^2,xiyi etc
    size_t n = vt.size();//=int n
    for (size_t i=0; i<n; i++){
        xsum=xsum+vt[i].x;//calculate sigma(xi)
        ysum=ysum+vt[i].y;//calculate sigma(yi)
        x2sum=x2sum+pow(vt[i].x,2);//calculate sigma(x^2i)
        xysum=xysum+vt[i].x*vt[i].y;//calculate sigma(xi*yi)
    }
    a=(n*xysum-xsum*ysum)/(n*x2sum-xsum*xsum);//calculate slope
    b=(x2sum*ysum-xsum*xysum)/(x2sum*n-xsum*xsum);//calculate intercept
}

void GetRadius(const vector <Vertex3D> &vt, double &R){
    double minx, maxx;
    double miny, maxy;
    double a, b;

    LinearFitting(vt, a, b);
    minx = maxx = vt[0].x;
    for (unsigned int i=0; i<vt.size(); i++){
        if (vt[i].x > maxx) maxx = vt[i].x;
        if (vt[i].x < minx) minx = vt[i].x;
    }
    miny = a*minx+b;
    maxy = a*maxx+b;

    R = 0.5*sqrt((maxx-minx)*(maxx-minx)+(maxy-miny)*(maxy-miny));
}

Vertex3D GetInterpolateEllipsePos(const vector <Vertex3D> vside, const Circle &up, const Circle &down, double y, double &currR){
    int i, k, n = vside.size();
    double vminx = vside[0].x, vminy = vside[0].y;
    double vmaxx = vside[n-1].x, vmaxy = vside[n-1].y;
    Vertex3D newPos;

    currR = down.r - (down.r - up.r)*y / up.vt.y;

    if (y<vminy)
        if(vminy==0)
            newPos.x = -down.r + currR;
        else
            newPos.x = -down.r + (down.r+vminx)*y/vminy + currR;
    else if (y>=vmaxy)
        if(up.vt.y==vmaxy)
            newPos.x = vmaxx + currR;
        else
            newPos.x = vmaxx + (up.vt.x-up.r-vmaxx)*(y-vmaxy)/(up.vt.y-vmaxy) + currR;
    else{
        k = 0;

        for(i=0; i<n; i++)
            if (y<vside[i].y){
                k = i;
                break;
            }
        if (vside[k].y==vside[k-1].y)
            newPos.x = vside[k-1].x + currR;
        else
            newPos.x = vside[k-1].x+(vside[k].x-vside[k-1].x)*(y-vside[k-1].y)/(vside[k].y-vside[k-1].y) + currR;
    }
    newPos.y = y;
    newPos.z = 0;

    return newPos;
}


void drawCircle(const Circle &cir){
    double angle;
    const double ANGLE_STEP = PI/60.0;
    Vertex3D vt;

    glColor3d(1, 0.5, 0);
    glLineWidth(3.0);

    for(angle=0; angle < 2.0*PI + ANGLE_STEP; angle+= ANGLE_STEP){
        vt.x= cir.vt.x + cir.r * cos(angle);
        vt.z= cir.vt.z + cir.r * sin(angle);
        vt.y = cir.vt.y;
    }

    glLineWidth(1.0);
    glEnable(GL_LIGHTING);
}

void drawCircles()
{
    size_t i;
    for (i=0; i<g_arrCircle.size(); i++)
        drawCircle(g_arrCircle[i]);
}


void Normalize(Vertex3D &v){
    double l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);

    if(l!=0)
    {
        v.x = v.x/l;
        v.y = v.y/l;
        v.z = v.z/l;
    }
}
void GetNormal(Vertex3D &NV, const Vertex3D v0, const Vertex3D v1, const Vertex3D v2){
    Vertex3D vec1,vec2;

    vec1.x = v1.x-v0.x;
    vec1.y = v1.y-v0.y;
    vec1.z = v1.z-v0.z;
    vec2.x = v2.x-v0.x;
    vec2.y = v2.y-v0.y;
    vec2.z = v2.z-v0.z;

    NV.x = vec1.y*vec2.z - vec1.z*vec2.y;
    NV.y = vec1.z*vec2.x - vec1.x*vec2.z;
    NV.z = vec1.x*vec2.y - vec1.y*vec2.x;

    Normalize(NV);
}

void GetNormal(Vertex3D &vup,Vertex3D &vdown,Vertex3D &vupNext,Vertex3D &vdownNext,
                        Vertex3D &vupNorm, Vertex3D &vdownNorm, Vertex3D &vupNextNorm, Vertex3D &vdownNextNorm){
    GetNormal(vupNorm, vup, vupNext, vdown);
    GetNormal(vdownNorm, vup, vdownNext, vdown);
    GetNormal(vupNextNorm, vup, vupNext, vdownNext);
    GetNormal(vdownNextNorm, vupNext, vdownNext, vdown);
}

/*Here We Go*/
void drawFaces(){
    Vertex3D a[g_arrCircle.size()][122];
    Vertex3D vupNorm, vdownNorm, vupNextNorm, vdownNextNorm;
    size_t i,j;
    double angle;
    const double ANGLE_STEP = PI/60.0;
    for(i=0; i<g_arrCircle.size(); i++)
        for(j=0,angle=0; angle < 2.0*PI + ANGLE_STEP; angle+= ANGLE_STEP,j++){
            a[i][j].x= g_arrCircle[i].vt.x + g_arrCircle[i].r * cos(angle);
            a[i][j].z= g_arrCircle[i].vt.z + g_arrCircle[i].r * sin(angle);
            a[i][j].y= g_arrCircle[i].vt.y;
        }
    glMaterialfv(GL_FRONT, GL_DIFFUSE, g_originalClr);

    glBegin(GL_QUADS);
    for(i=0; i<g_arrCircle.size()-1; i++)
        for(j=0; j<121; j++){
            GetNormal(a[i][j], a[i][j+1], a[i+1][j], a[i+1][j+1], vupNorm, vdownNorm, vupNextNorm, vdownNextNorm);

            glVertex3d(a[i][j].x, a[i][j].y, a[i][j].z);
            glNormal3d(vupNorm.x, vupNorm.y, vupNorm.z);

            glVertex3d(a[i][j+1].x, a[i][j+1].y, a[i][j+1].z);
            glNormal3d(vupNextNorm.x, vupNextNorm.y, vupNextNorm.z);

            glVertex3d(a[i+1][j+1].x, a[i+1][j+1].y, a[i+1][j+1].z);
            glNormal3d(vdownNextNorm.x, vdownNextNorm.y, vdownNextNorm.z);

            glVertex3d(a[i+1][j].x, a[i+1][j].y, a[i+1][j].z);
            glNormal3d(vdownNorm.x, vdownNorm.y, vdownNorm.z);
        }
    glEnd();
}

int BmpFileLoader(char *fileName)
{
    FILEHEADER imgHead;
    BGR pixel;
    fstream imgFile;

    imgFile.open(fileName, ios::in|ios::binary);

    if(!imgFile)
    {
        cerr<<"File opening or creating error!"<<endl;
        return 0;
    }

    imgFile.read((char *)(&imgHead), sizeof(FILEHEADER));
    cout << "Image size:" << imgHead.width << "*" << imgHead.height << endl;
    if(imgHead.width%4!=0) return 0;
    for(int i=0; i<(imgHead.width*imgHead.height); i++)
    {
        /*图像的位数通常是8位,2^8 = 256种,0-255刚好是256种颜色*/
        imgFile.read((char *)(&pixel), sizeof(BGR));
        if (pixel == BGR(0, 0, 255)){
            //Red对应底部那条线
            g_downVt.push_back(Vertex3D(i%imgHead.width, i/imgHead.width, 0));
        }
        else if (pixel == BGR(0, 255, 0)){
            //Green对应角顶点的两个pixel
            g_upVt.push_back(Vertex3D(i%imgHead.width, i/imgHead.width, 0));
        }
        else if (pixel == BGR(255, 0, 0)){
            //Blue在对应边缘
            g_sideVt.push_back(Vertex3D(i%imgHead.width, i/imgHead.width, 0));
        }

    }
    imgFile.close();
    return 1;
}

void PreprocessSketchVertices()
{
    double ymin, ymax, xmin, xmax;

    g_sideSketchNo = g_sideVt.size();//Blue
    g_upSketchNo = g_upVt.size();//Green
    g_downSketchNo = g_downVt.size();//Red

    xmin = xmax = g_sideVt[0].x;
    ymin = ymax = g_sideVt[0].y;

    for (int i=1; i<g_sideSketchNo; i++)
    {
        if (g_sideVt[i].y>ymax) ymax = g_sideVt[i].y;
        if (g_sideVt[i].y<ymin) ymin = g_sideVt[i].y;
        if (g_sideVt[i].x>xmax) xmax = g_sideVt[i].x;
        if (g_sideVt[i].x<xmin) xmin = g_sideVt[i].x;
    }

    for (int i=0; i<g_sideSketchNo; i++)
    {
        g_sideVt[i].x = g_scale*(g_sideVt[i].x-xmin)/(ymax-ymin);
        g_sideVt[i].y = g_scale*(g_sideVt[i].y-ymin)/(ymax-ymin);
    }

    for (int i=0; i<g_downSketchNo; i++)
    {
        g_downVt[i].x = g_scale*(g_downVt[i].x-xmin)/(ymax-ymin);
        g_downVt[i].y = g_scale*(g_downVt[i].y-ymin)/(ymax-ymin);
    }

    for (int i=0; i<g_upSketchNo; i++)
    {
        g_upVt[i].x = g_scale*(g_upVt[i].x-xmin)/(ymax-ymin);
        g_upVt[i].y = g_scale*(g_upVt[i].y-ymin)/(ymax-ymin);
    }

    GetRadius(g_upVt, g_upCircle.r);
    GetRadius(g_downVt, g_downCircle.r);

    g_downCircle.vt.x  = g_sideVt[0].x -  g_downCircle.r;
    g_downCircle.vt.y = g_sideVt[0].y;

    g_upCircle.vt.x  = g_sideVt[g_sideSketchNo-1].x -  g_upCircle.r;
    g_upCircle.vt.y = g_sideVt[g_sideSketchNo-1].y;

    cout << g_upCircle.r << "," << g_downCircle.r << endl;
}

void ComputeCircleInfo(){
    Circle curr;
    double y;

    for(int i=0; i<=g_sliceNo; i+=4)
    {
        y = i*g_upCircle.vt.y/g_sliceNo;
        curr.vt = GetInterpolateEllipsePos(g_sideVt, g_upCircle, g_downCircle, y, curr.r);
        g_arrCircle.push_back(curr);
    }
}

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

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, ar, 1.0, 100.0);

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

    //drawCircles();
    drawFaces();
    //drawControlPoints();

    glutSwapBuffers();
}


static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 27 :
        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);
    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");

    glutCreateWindow("Sketch cylinder");

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

    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, g_ambDiff);
    glMaterialfv(GL_FRONT, GL_SPECULAR,  g_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, g_shininess);

    glShadeModel(GL_SMOOTH);

    printf("Loading \"%s\"...\n", g_strFileName);
    BmpFileLoader(g_strFileName);
    PreprocessSketchVertices();
    ComputeCircleInfo();

    glutMainLoop();

    return EXIT_SUCCESS;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

行秋

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

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

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

打赏作者

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

抵扣说明:

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

余额充值