最近看到dx中的sample中有用顶点数组实现的圆柱体贴图的程序。于是自己写了一个圆柱体的类,支持生成纹理坐标,法线,坐标。
构造圆柱体的参数主要有:
- 水平分段数(就是上下圆的分段数目),
- 高度上的分段数目,(至少有两段:上下两个圆面)
- 半径,
- 高度。
顶点生成的原理:将圆柱面展开就是一个矩形。而纹理也是一个矩形区域,需要注意的是圆柱面是一个首尾相接的封闭面。
效果图如下:
代码:
头文件
- #ifndef CYLINDER_H
- #define CYLINDER_H
- struct Vertex3f
- {
- float x;
- float y;
- float z;
- float nx;
- float ny;
- float nz;
- float u;
- float v;
- void setValue(float x_, float y_, float z_)
- {
- x = x_;
- y = y_;
- z = z_;
- }
- void setNormal(float x_, float y_, float z_)
- {
- nx = x_;
- ny = y_;
- nz = z_;
- }
- void setTexture(float u_, float v_)
- {
- u = u_;
- v = v_;
- }
- };
- enum REND_MODE
- {
- SOLID = 3000,
- WIRE = 3001
- };
- /*
- * Cylinder created by RYF. [11/25/2009]
- */
- class Cylinder
- {
- public:
- Cylinder(float r, float h, int l, int v);
- ~Cylinder();
- void Render(REND_MODE mode);
- void PrintMatrices();
- protected:
- Cylinder(){};
- void RenderSlice( const Vertex3f& v1,
- const Vertex3f& v2,
- const Vertex3f& v3,
- const Vertex3f& v4,
- int i, int j,
- REND_MODE mode );
- void RenderSliceNormal( const Vertex3f& v1,
- const Vertex3f& v2,
- const Vertex3f& v3,
- const Vertex3f& v4 );
- void _getMatrices();
- void _setupVertexTexcoord();
- void _getVertex(float alpha, float hgh, Vertex3f& vertex);
- unsigned int texId; //纹理ID
- bool isHasTex; //是否加载纹理
- float radius; //半径
- float height; //高度
- int lSlice; //水平分段数
- int vSlice; //垂直分段数
- Vertex3f* pVertexBuffer; //顶点数据
- float* pTexcoord; //最后一列的纹理坐标
- };
- #endif
cpp文件:
- #include "Cylinder.h"
- #include <gl/glut.h>
- #include <iostream>
- #include <iomanip>
- #include <cmath>
- // ----------------------------------------------------------------
- // Description: 重载构造函数
- // Para info: r为上下圆的半径
- // h为高度
- // l为上下圆的分段数
- // v为高度上圆柱的段数v>=2
- // ----------------------------------------------------------------
- Cylinder::Cylinder(float r, float h, int l, int v)
- : radius(r)
- , height(h)
- , lSlice(l)
- , vSlice(v)
- , texId(0)
- , isHasTex(false)
- , pVertexBuffer(0)
- , pTexcoord(0)
- {
- pVertexBuffer = new Vertex3f[vSlice*lSlice];
- pTexcoord = new float[vSlice];
- _setupVertexTexcoord();
- _getMatrices();
- PrintMatrices();
- }
- Cylinder::~Cylinder()
- {
- if (pVertexBuffer)
- {
- delete pVertexBuffer;
- pVertexBuffer = 0;
- }
- }
- // ----------------------------------------------------------------
- // Description: 圆柱体绘制函数
- // 绘制按照逆时钟:
- // v4 v3
- //
- // v1 v2
- // ----------------------------------------------------------------
- void Cylinder::Render(REND_MODE mode)
- {
- int i(0);
- int j(0);
- // 绘制圆柱体
- for (i=0; i<vSlice-1; i++)
- {
- for (j=0; j<lSlice-1; j++)
- {
- RenderSlice( pVertexBuffer[(i+1)*lSlice+j],
- pVertexBuffer[(i+1)*lSlice+j+1],
- pVertexBuffer[i*lSlice+j+1],
- pVertexBuffer[i*lSlice+j],
- i, j,
- mode );
- }
- RenderSlice( pVertexBuffer[(i+1)*lSlice+j], //第二行末端
- pVertexBuffer[(i+1)*lSlice], //第二行始端
- pVertexBuffer[i*lSlice], //第一行始端
- pVertexBuffer[i*lSlice+j], //第一行末端
- i, j+1,
- mode );
- }
- }
- // ----------------------------------------------------------------
- // Description: 绘制四边形函数
- // 函数参数顺序: 1 2 3 4
- // GL_TRIANGLE_STRIP的绘制顺序:1 2 4 3
- // 四个v参数提供:位置和法线数据
- // i,j提供纹理坐标数据
- // 1, 4为要特殊处理的点
- // ----------------------------------------------------------------
- void Cylinder::RenderSlice( const Vertex3f& v1,
- const Vertex3f& v2,
- const Vertex3f& v3,
- const Vertex3f& v4,
- int i, int j,
- REND_MODE mode )
- {
- glDisable(GL_LIGHTING);
- switch(mode)
- {
- case SOLID:
- glBegin(GL_TRIANGLE_STRIP);
- //glBegin(GL_POLYGON);
- break;
- case WIRE:
- glBegin(GL_LINE_LOOP);
- break;
- }
- glTexCoord2f( v1.u, v1.v );
- glVertex3f(v1.x, v1.y, v1.z); /*glNormal3f(v1.nx, v1.ny, v1.nz);*/
- //特殊处理v2的配置
- if (j==lSlice)
- {
- glTexCoord2f( 1.0f, pTexcoord[i+1] );
- glVertex3f(v2.x, v2.y, v2.z); /*glNormal3f(v2.nx, v2.ny, v2.nz);*/
- }
- else
- {
- glTexCoord2f( v2.u, v2.v );
- glVertex3f(v2.x, v2.y, v2.z); /*glNormal3f(v2.nx, v2.ny, v2.nz);*/
- }
- glTexCoord2f( v4.u, v4.v );
- glVertex3f(v4.x, v4.y, v4.z); /*glNormal3f(v4.nx, v4.ny, v4.nz);*/
- //特殊处理v3的配置
- if (j==lSlice)
- {
- glTexCoord2f( 1.0f, pTexcoord[i] );
- glVertex3f(v3.x, v3.y, v3.z); /*glNormal3f(v3.nx, v3.ny, v3.nz);*/
- }
- else
- {
- glTexCoord2f( v3.u, v3.v );
- glVertex3f(v3.x, v3.y, v3.z); /*glNormal3f(v3.nx, v3.ny, v3.nz);*/
- }
- glEnd();
- // 显示法线
- // RenderSliceNormal( v1, v2, v3, v4 );
- glEnable(GL_LIGHTING);
- }
- void Cylinder::RenderSliceNormal( const Vertex3f& v1, const Vertex3f& v2, const Vertex3f& v3, const Vertex3f& v4 )
- {
- glBegin(GL_LINES);
- glVertex3f(v1.x, v1.y, v1.z);
- glVertex3f(v1.nx + v1.x, v1.ny+v1.y, v1.nz+v1.z);
- glEnd();
- glBegin(GL_LINES);
- glVertex3f(v2.x, v2.y, v2.z);
- glVertex3f(v2.nx + v2.x, v2.ny+v2.y, v2.nz+v2.z);
- glEnd();
- glBegin(GL_LINES);
- glVertex3f(v3.x, v3.y, v3.z);
- glVertex3f(v3.nx + v3.x, v3.ny+v3.y, v3.nz+v3.z);
- glEnd();
- glBegin(GL_LINES);
- glVertex3f(v4.x, v4.y, v4.z);
- glVertex3f(v4.nx + v4.x, v4.ny+v4.y, v4.nz+v4.z);
- glEnd();
- }
- // ----------------------------------------------------------------
- // Description: 根据vSlice和lSlice生成纹理坐标
- //
- // ----------------------------------------------------------------
- void Cylinder::_setupVertexTexcoord()
- {
- if (!pVertexBuffer || !pTexcoord)
- return;
- float ds = 1.0f / lSlice; //x,u 方向
- float dt = 1.0f / (vSlice-1); //y,v 方向
- for (int i=0; i<vSlice; i++)
- {
- for (int j=0; j<lSlice; j++)
- {
- pVertexBuffer[j + i*lSlice].u = j*ds;
- pVertexBuffer[j + i*lSlice].v = i*dt;
- }
- // 最后一列的纹理横坐标全部为:1.0
- // pTexcoord中只是纵坐标
- pTexcoord[i] = i * dt;
- }
- }
- // ----------------------------------------------------------------
- // Description: 根据角度和高度求取顶点的坐标
- // Para info: theta为弧度数,圆中的角度
- // hgh为高度
- // ----------------------------------------------------------------
- void Cylinder::_getVertex(float theta, float hgh, Vertex3f& vertex)
- {
- float cosTheta = cos(theta);
- float sinTheta = sin(theta);
- // setup position coordinate
- vertex.x = radius * cosTheta;
- vertex.y = hgh;
- vertex.z = radius * sinTheta;
- // setup normal coordinate
- vertex.nx = cosTheta;
- vertex.ny = 0;
- vertex.nz = sinTheta;
- }
- // ----------------------------------------------------------------
- // Description: 求取圆柱的顶点阵列
- // ----------------------------------------------------------------
- void Cylinder::_getMatrices()
- {
- const float pi = 3.1415926;
- float angle = 2.0f * pi / static_cast<float>(lSlice);
- // 注意分母为高度段数减1
- float span = height / static_cast<float>(vSlice-1);
- for (int v=0; v<vSlice; v++)
- {
- float y = span * v;
- for (int l=0; l<lSlice; l++)
- {
- float x = angle * static_cast<float>(l);
- _getVertex(x, y, pVertexBuffer[l + v*lSlice]);
- }
- }
- }
- // ----------------------------------------------------------------
- // Description: 打印matrices中的元素
- // ----------------------------------------------------------------
- void Cylinder::PrintMatrices()
- {
- using namespace std;
- for (int i=0; i<vSlice*lSlice; i++)
- {
- cout << "//---------------------------------------------------------------------" << endl;
- cout << "row = " << i / lSlice << ".";
- cout << "coll = " << i % lSlice << endl;
- cout << "position(x, y, z) = " << setprecision (7) << pVertexBuffer[i].x << " "
- << setprecision (7) << pVertexBuffer[i].y << " "
- << setprecision (7) << pVertexBuffer[i].z << endl;
- cout << "texture(u, v) = " << setprecision (7) << pVertexBuffer[i].u << " "
- << setprecision (7) << pVertexBuffer[i].v << endl;
- cout << "//---------------------------------------------------------------------" << endl;
- }
- }
测试程序如下,要运行起来,还必须加一个bmpLoader的程序库,可以到这里下载一个简单的,里面有自带的bmp图片。
http://users.ox.ac.uk/~orie1330/bmploader.html
代码:
- #include <GL/glut.h>
- #include <cstdio>
- #include <cstdlib>
- #include "Cylinder.h"
- #include "BMPLoader.h"
- /* Create checkerboard texture */
- #define checkImageWidth 64
- #define checkImageHeight 64
- static GLubyte checkImage[checkImageHeight][checkImageWidth][4];
- static GLuint texName;
- Cylinder g_cylinder(0.5f, 1.3f, 30, 5);
- static float angleX = 0.0f;
- static float angleY = 0.0f;
- //
- void makeCheckImage(void)
- {
- int i, j, c;
- for (i = 0; i < checkImageHeight; i++) {
- for (j = 0; j < checkImageWidth; j++) {
- c = ((((i&0x8)==0)^((j&0x8))==0))*255;
- checkImage[i][j][0] = (GLubyte) c;
- checkImage[i][j][1] = (GLubyte) c;
- checkImage[i][j][2] = (GLubyte) c;
- checkImage[i][j][3] = (GLubyte) 255;
- }
- }
- }
- void init(void)
- {
- glClearColor (0.0, 0.0, 0.0, 0.0);
- glShadeModel(GL_SMOOTH);
- glEnable(GL_DEPTH_TEST);
- //makeCheckImage();
- BMPClass bmp;
- BMPLoad("bmp16.bmp", bmp);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glGenTextures(1, &texName);
- glBindTexture(GL_TEXTURE_2D, texName);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- /*
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, / *checkImage* /bmp.bytes);
- */
- glTexImage2D(GL_TEXTURE_2D,0,3,bmp.width,bmp.height,0,GL_RGB,GL_UNSIGNED_BYTE,bmp.bytes);
- }
- void display(void)
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glEnable(GL_TEXTURE_2D);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
- glBindTexture(GL_TEXTURE_2D, texName);
- glPushMatrix();
- {
- glRotatef(angleX, 1.0f, 0.0f, 0.0f);
- glRotatef(angleY, 0.0f, 1.0f, 0.0f);
- g_cylinder.Render(SOLID);
- }glPopMatrix();
- glutSwapBuffers();
- glDisable(GL_TEXTURE_2D);
- glutPostRedisplay();
- }
- void reshape(int w, int h)
- {
- glViewport(0, 0, (GLsizei) w, (GLsizei) h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 30.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, -3.6f);
- }
- void keyboard (unsigned char key, int x, int y)
- {
- switch (key) {
- case 27:
- exit(0);
- break;
- case 'x':
- angleX += 5.0f;
- break;
- case 's':
- angleX -= 5.0f;
- case 'y':
- angleY += 4.0f;
- break;
- case 'h':
- angleY -= 4.0f;
- break;
- default:
- break;
- }
- glutPostRedisplay();
- }
- int main(int argc, char** argv)
- {
- glutInit(&argc, argv);
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
- glutInitWindowSize(250, 250);
- glutInitWindowPosition(100, 100);
- glutCreateWindow(argv[0]);
- init();
- glutDisplayFunc(display);
- glutReshapeFunc(reshape);
- glutKeyboardFunc(keyboard);
- glutMainLoop();
- return 0;
- }
PS:OpenGL绘制什么都得DIY。