问题描述
根据参考程序,读取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;
}