最近看到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。