opengl中用三角形opengl画球体函数,运行无法显示图形怎么解决

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
不相信平凡
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(5423)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_087070',
blogTitle:'OpenGL中对球体使用简单的灯光效果',
blogAbstract:'源码来自《OpenGL编程指南》:\r\n&\r\n#include &glut.h&& //************************************//才才//球的光影效果////************************************\r\nvoid init(void){&GLfloat mat_sp[]={1.0,1.0,1.0,1.0};&GLfloat mat_sh[]={50.0};&GLfloat light_p[]={1,1,1,0};&GLfloat yellow_l[]={1,1,0,1};&GLfloat lmodel_a[]={0.1,0.1,0.1,1.0};',
blogTag:'球,灯光,opengl',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:4,
publishTime:4,
permalink:'blog/static/',
commentCount:2,
mainCommentCount:1,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'不相信平凡',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}OpenGL中参数方程绘制(球体)
首先要声明的是如果想运行上面的程序,在你的计算机上必须装有GLUT开发工具,并要将GLUT的头文件和库文件添加到你开发环境的头文件和库文件的目录中,并且让编译器能找到GLUT的动态连接库,即GLUT32.DLL。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
具体请参见GLUT的安装方法……
先是Sphere.h,包含了windows.h头文件和一些其他可能用到的标准C头文件,可以直接写到Sphere.cpp里面。接下来在Sphere.cpp里面包含Sphere.h和GLUT的头文件glut.h
2 自定义宏、类型和结构体
#define pi 3.1415926
定义圆周率pi,后面计算坐标时要用到
#define SOLID&3000
#define WIRE&&3001
定义画球的模式,SOLID表示画实面球,WIRE表示画网格球
typedef int SPHERE_MODE;
定义画球模式的类型,其实是个int整形
typedef struct Point3f
定义记录空间点坐标的结构体point,GLfloat类型其实就是GLUT里面定义的float类型
3 函数声明和实现
void init(void);
void reshape(int w,int h);
void display(void);
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point
void drawSlice(point &p1,point &p2,point &p3,point
&p4,SPHERE_MODE mode);
point* getPointMatrix(GLfloat radius,GLint slices);
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE
init()、reshape()和display()这3个函数是OpenGL绘图的一般过程所用的函数,这3个函数的函数名可以不同,但形参部分的形参类型必须一样(注),且这3个函数在主函数中是依次调用的。
(注: void reshape(int w,int h); 也可作 void resizeWindow(int width,int
在init()函数里,分别对屏幕背景颜色,投影模式,光照,光原位置等进行了设置,并启用纹理,灯光和深度测试;
reshape()函数里面,对视口和投影矩阵进行设置;
接下来是display()函数,先移动原点坐标到窗口中心位置,再对所要绘制的图形进行一定角度的旋转,设置绘图的颜色,然后调用drawSphere()绘制球体。
关于init(),reshape()和display()这3个函数里面的具体过程,请参见OpenGL的相关教程。
下面的几个函数是绘制球体所用的,这里先简略给出他们的作用,具体实现将在下一部分详细解析:
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point
&p)&函数:
根据半径radius,a角(半径与Z轴正向的夹角),b角(半径在xy平面的投影与x轴正向的夹角)算出球面上点的坐标并记录到point类型的参数p中。
void drawSlice(point &p1,point &p2,point
&p3,point &p4,SPHERE_MODE mode)&函数:
根据实际调用时提供的4个点的坐标在空间画四边形,并用mode参数确定所画四边形是空心还是实心的。
point* getPointMatrix(GLfloat radius,GLint
slices)&函数:
根据提供的半径radius和分块数slices计算出一系列球面上的点的坐标,储存在一个动态创建的矩阵中,并返回指向该矩阵的指针,要求的分块数越多,计算的点就越多,球面就越光滑,矩阵也就越大。
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE
mode)&函数:
绘球所调用的函数,根据提供的半径radius和分块数slices在窗体中绘制球体,分块数越大,球面越光滑,mode参数用于确定要绘制实面球还是网格球。
(继续写……)
4 绘球部分
这里绘制的球,其实是个球面。把球面看成是由很多个小的四边形平面构成的,这样就可以通过绘制这些小的四边形平面来构成整个球面。而对于两极部分,需要用三角形将球面两端封起来,如果把这些三角型看作是有两个顶点重合的四边形,问题就得到进一步的简化。因此只需知道球面上一些列点的空间坐标,就可以利用这些点来绘制四边形从而得到整个球面。把整个球面展开,可见球面上这些点构成的是一个矩阵,而这个矩阵中纵横相邻的四个元素的坐标正好构成的就是球面上的一个小平面。
如何获取球面上点的坐标?
利用球的空间坐标参数方程
x=r·sin(α)·cos(β)
y=r·sin(α)·sin(β)
z=r·cos(α)
r是球的半径,α角是半径与Z轴正向的夹角,β角是半径在xy平面的投影与x轴正向的夹角,他们的取值范围是
0≤r≤∞&&&&0≤α≤π&&&&0≤β≤2π
因此函数 getPoint()
就是通过此参数方程来获得空间点的坐标;
getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p)
&p.x=radius*sin(a*pi/180.0)*cos(b*pi/180.0);
&p.y=radius*sin(a*pi/180.0)*sin(b*pi/180.0);
&p.z=radius*cos(a*pi/180.0);
&return 1;
值得注意的是 sin() 和 cos()
函数的参数是弧度角,而形参传入的是角度角,因此需要进行转换。
获取球面坐标矩阵
形参slices确定取点的间隔大小。
α角的大小是0~180度,β角的大小是0~360度,因此我们在z轴方向,每隔180/(slice-1)取一横列的点作为α角,在垂直于z轴的平面上,每隔360/slice取一纵列的点作为β角。由于β角的范围是α角的两倍,所以矩阵横向取点的个数是纵向取点的两倍。接下来就是动态分配内存空间,然后通过循环为矩阵中的元素,即球面上的点的坐标赋值。这里调用 getPoint()
这个函数来计算空间点坐标。虽然分配出来的空间是线性的,但可以通过元素的下标计算确定该元素在线性空间的位置。
point* getPointMatrix(GLfloat radius,GLint slices)
&int i,j,w=2*slices,h=
&float a=0.0,b=0.0;
&float hStep=180.0/(h-1);
&float wStep=360.0/w;
&int length=w*h;
&matrix=(point
*)malloc(length*sizeof(point));
&if(!matrix)return NULL;
&for(a=0.0,i=0;i
&&for(b=0.0,j=0;j
&&&getPoint(radius,a,b,matrix[i*w+j]);&
drawSlice(point &p1,point &p2,point &p3,point
&p4,SPHERE_MODE mode)
&switch(mode)
&case SOLID:
&&glBegin(GL_QUADS);
&case WIRE:
&&glBegin(GL_LINE_LOOP);
&glColor3f(1,0,0);
&glVertex3f(p1.x,p1.y,p1.z);
&glVertex3f(p2.x,p2.y,p2.z);
&glVertex3f(p3.x,p3.y,p3.z);
&glVertex3f(p4.x,p4.y,p4.z);
形参mode确定了绘图的模式,当其为SOLID时,用GL_QUADS模式绘实平面,当为WIRE时,就用
GL_LINE_LOOP绘制首尾相连的四条线。注意最后要用
glEnd() 结束对该平面的绘制,否则会得到“怪异”的结果。
drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode)
&int i=0,j=0,w=2*slices,h=
&mx=getPointMatrix(radius,slices);
&if(!mx)return 0;
&&for(j=0;j
&&&drawSlice(mx[i*w+j],mx[i*w+j+1],mx[(i+1)*w+j+1],mx[(i+1)*w+j],mode);
&&drawSlice(mx[i*w+j],mx[i*w],mx[(i+1)*w],mx[(i+1)*w+j],mode);
&free(mx);
&return 1;
这就是绘制球面的主框架函数了,先调用
getPointMatrix() 来产生球面点矩阵,并用局部变量mx记录该矩阵在内存中的位置。接下来就是把该矩阵上记录的点用
drawSlice()
函数绘成平面。例如mx[0,0],mx[0,1],mx[1,1],mx[1,0]这四个元素中记录的就是沿逆时针方向构成球面上第一个平面的4个点了。要注意的是,在矩阵的横向上,最后一列点需要和第一列点连起来再绘制平面,这样产生的球面才是封闭的。在函数最后要把动态分配的内存空间释放掉,不然会造成内存泄露。如果绘制成功,函数返回1,否则返回0。
(终于写到结尾了……)
有了上面几个函数就可以在直接调用 drawSphere()
函数来绘制不同大小,不同精度和不同模式的球了。
程序的代码:
#pragma once
Windows Header Files
// C RunTime Header Files
#include #include
Sphere.cpp
#include "Sphere.h"
#define pi 3.1415926
#define SOLID&3000
#define WIRE&&3001
typedef int SPHERE_MODE;
typedef struct Point3f
init(void);
void reshape(int w,int h);
void display(void);
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point
void drawSlice(point &p1,point &p2,point &p3,point
&p4,SPHERE_MODE mode);
point* getPointMatrix(GLfloat radius,GLint slices);
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE
APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE
hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
&glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB |
GLUT_DEPTH);&
&glutInitWindowSize (500, 500);
&glutInitWindowPosition (100, 100);
&glutCreateWindow("Sphere");
&glutReshapeFunc(reshape);
&glutDisplayFunc(display);
&glutMainLoop();
&return 0;
void init (void)
&glClearColor (0.0, 0.0, 0.0, 0.0);
&glClearDepth(1);
&glShadeModel(GL_SMOOTH);
&GLfloat _ambient[]={1.0,1.0,1.0,1.0};
&GLfloat _diffuse[]={1.0,1.0,1.0,1.0};
&GLfloat _specular[]={1.0,1.0,1.0,1.0};
&GLfloat _position[]={200,200,200,0};
&glLightfv(GL_LIGHT0,GL_AMBIENT,_ambient);
&glLightfv(GL_LIGHT0,GL_DIFFUSE,_diffuse);
&glLightfv(GL_LIGHT0,GL_SPECULAR,_specular);
&glLightfv(GL_LIGHT0,GL_POSITION,_position);
&glEnable(GL_TEXTURE_2D);
&glEnable(GL_LIGHTING);
&glEnable(GL_LIGHT0);
&glEnable(GL_DEPTH_TEST);
&glHint(GL_PERSPECTIVE_CORRECTION_HINT,
GL_NICEST);
void reshape(int w, int h)
&glViewport (0, 0, (GLsizei) w, (GLsizei)
&glMatrixMode(GL_PROJECTION);
&glLoadIdentity();
&glOrtho(0.0, 500, 0.0, 500, -500, 500);
&glMatrixMode(GL_MODELVIEW);
&glLoadIdentity();
void display(void)
&glMatrixMode(GL_MODELVIEW);
&glLoadIdentity();
(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
&glTranslated(250,250,0);
&glRotated(30,1,0,0);
&glRotated(60,0,1,0);
&glRotated(90,0,0,1);
&glColor3f(1.0,1.0,1.0);
&drawSphere(200,20,WIRE);&
&glFlush();
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p)
&p.x=radius*sin(a*pi/180.0)*cos(b*pi/180.0);
&p.y=radius*sin(a*pi/180.0)*sin(b*pi/180.0);
&p.z=radius*cos(a*pi/180.0);
&return 1;
void drawSlice(point &p1,point &p2,point &p3,point
&p4,SPHERE_MODE mode)
&switch(mode)
&case SOLID:
&&glBegin(GL_QUADS);
&case WIRE:
&&glBegin(GL_LINE_LOOP);
&glColor3f(1,0,0);
&glVertex3f(p1.x,p1.y,p1.z);
&glVertex3f(p2.x,p2.y,p2.z);
&glVertex3f(p3.x,p3.y,p3.z);
&glVertex3f(p4.x,p4.y,p4.z);
point* getPointMatrix(GLfloat radius,GLint slices)
&int i,j,w=2*slices,h=
&float a=0.0,b=0.0;
&float hStep=180.0/(h-1);
&float wStep=360.0/w;
&int length=w*h;
&matrix=(point
*)malloc(length*sizeof(point));
&if(!matrix)return NULL;
&for(a=0.0,i=0;i
&&for(b=0.0,j=0;j
&&&getPoint(radius,a,b,matrix[i*w+j]);&
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode)
&int i=0,j=0,w=2*slices,h=
&mx=getPointMatrix(radius,slices);
&if(!mx)return 0;
&&for(j=0;j
&&&drawSlice(mx[i*w+j],mx[i*w+j+1],mx[(i+1)*w+j+1],mx[(i+1)*w+j],mode);
&&drawSlice(mx[i*w+j],mx[i*w],mx[(i+1)*w],mx[(i+1)*w+j],mode);
&free(mx);
&return 1;
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。在看OpenGL红皮书,看到生成球体这节,讲了很多,总感觉不如自己动手写一些代码来的实在,用OpenGL中三角形模拟球形生成.主要要点,模型视图变换,多边形表面环绕一致性,矩阵堆栈.先贴上代码.
虽然是用F#写的,但是处理全是过程式的,很好理解.
1 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.dll"
2 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.GLControl.dll"
4 open System
5 open System.Collections.Generic
6 open System.Windows.Forms
7 open System.Threading
8 open System.Drawing
9 open System.Drawing.Imaging
10 open OpenTK
11 open OpenTK.Graphics
12 open OpenTK.Graphics.OpenGL
14 type loopForm() as form=
inherit Form()
let mutable x = 5.f
let mutable y = 5.f
let mutable z = 5.f
let offest = 1.f
let glControl = new OpenTK.GLControl()
let textX= new TextBox()
let textY= new TextBox()
let textZ= new TextBox()
let textLR = new TextBox()
let textUD= new TextBox()
let textInfo = new TextBox()
let labelX= new Label()
let labelY= new Label()
let labelZ= new Label()
let labelLR = new Label()
let labelUD= new Label()
let mutable first = 0
let mutable buffer = 0
let list = 0
let scale = 3.f
form.SuspendLayout()
glControl.Location &- new Point(10,40)
glControl.Size &- new Size(400,300)
glControl.BackColor &- Color.Red
glControl.Resize.Add(form.resize)
glControl.Paint.Add(form.paint)
form.MouseWheel.Add(form.MouseDown)
form.ClientSize &- new Size(600,400)
form.Text &- "opengl"
form.StartPosition &- FormStartPosition.Manual
form.Location &- new Point(1200,600)
form.Controls.Add(glControl)
form.ResumeLayout(false)
labelX.Location &- new Point(420,40)
labelY.Location &- new Point(420,70)
labelZ.Location &- new Point(420,100)
labelLR.Location &- new Point(420,130)
labelUD.Location &- new Point(420,160)
labelX.Text &- "X:"
labelY.Text &- "Y:"
labelZ.Text &- "Z:"
labelLR.Text
&- "水平:"
labelUD.Text &-"上下:"
textX.Location &- new Point(460,40)
textY.Location &- new Point(460,70)
textZ.Location &- new Point(460,100)
textLR.Location &- new Point(460,130)
textUD.Location &- new Point(460,160)
textInfo.Location &- new Point(420,190)
textInfo.Width &- 140
form.Controls.Add(textX)
form.Controls.Add(textY)
form.Controls.Add(textZ)
form.Controls.Add(textLR)
form.Controls.Add(textUD)
form.Controls.Add(labelX)
form.Controls.Add(labelY)
form.Controls.Add(labelZ)
form.Controls.Add(labelLR)
form.Controls.Add(labelUD)
form.Controls.Add(textInfo)
//#endregion
override v.OnLoad e =
base.OnLoad e
GL.ClearColor Color.MidnightBlue
Application.Idle.Add(v.AIdle)
textX.TextChanged.Add(form.TextChange)
textY.TextChanged.Add(form.TextChange)
textZ.TextChanged.Add(form.TextChange)
textLR.TextChanged.Add(form.TextChange)
textUD.TextChanged.Add(form.TextChange)
//踢除正反面
//GL.Enable EnableCap.CullFace
//GL.CullFace CullFaceMode.Back
//指定正反面
GL.FrontFace FrontFaceDirection.Ccw
//设置材料面填充模式
GL.PolygonMode(MaterialFace.Front,PolygonMode.Fill)
GL.PolygonMode(MaterialFace.Back,PolygonMode.Line)
//启用数组功能.
GL.EnableClientState(ArrayCap.VertexArray)
//GL.EnableClientState(ArrayCap.ColorArray)
GL.EnableClientState(ArrayCap.IndexArray)
101 //#region ""
member v.resize (e:EventArgs) =
GL.Viewport(0,0,glControl.ClientSize.Width,glControl.ClientSize.Height)
let aspect = float32 glControl.ClientSize.Width /float32 glControl.ClientSize.Height
let mutable projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4,aspect,0.1f,64.f)
GL.MatrixMode MatrixMode.Projection
GL.LoadMatrix(&projection)
member v.paint (e:PaintEventArgs) =
v.Render()
member v.AIdle (e:EventArgs) =
while (glControl.IsIdle) do
v.Render()
member v.TextChange (e:EventArgs) =
x &- v.UIValue(textX)
y &- v.UIValue(textY)
z &- v.UIValue(textZ)
member v.MouseDown(e:MouseEventArgs) =
match v.ActiveControl with
| :? TextBox as t1 -&
let mutable t = v.UIValue(t1)
t &- t + float32 e.Delta * offest * 0.01f
t1.Text &- t.ToString()
v.Text &- v.ActiveControl.Text
let state =float32 e.Delta * offest * 0.01f
x&- x+state
y&- y + state
z &- z + state
member x.UIValue
with get (text:TextBox) =
let mutable value = 0.f
if System.Single.TryParse(text.Text,&value) then
value &- value
and set (text:TextBox) (value:float32) = text.Text&- value.ToString()
member v.ShowUI =
textX.Text &- x.ToString()
textY.Text &- y.ToString()
textZ.Text &- z.ToString()
textLR.Text &- v.UIValue(textLR).ToString()
textUD.Text &- v.UIValue(textUD).ToString()
member v.Normal (c:Vector3) =
c.Normalize()
member v.Subdivide (v1:Vector3,v2:Vector3,v3:Vector3) =
let vs = Array.create 6 Vector3.Zero
vs.[0] &- v1
vs.[1] &- v.Normal( Vector3.Lerp(v1,v2,0.5f))
vs.[2] &- v.Normal( Vector3.Lerp(v3,v1,0.5f))
vs.[3] &- v2
vs.[4] &- v.Normal( Vector3.Lerp(v2,v3,0.5f))
vs.[5] &- v3
let is = Array.create 12 0
is.[0] &- 0
is.[1] &- 1
is.[2] &- 2
is.[3] &- 2
is.[4] &- 1
is.[5] &- 4
is.[6] &- 4
is.[7] &- 1
is.[8] &- 3
is.[9] &- 2
is.[10] &-4
is.[11] &- 5
168 //#endregion
member v.CreatePane (angle:float32,x,y,z) =
GL.PushMatrix()
GL.Color3(Color.Green)
GL.Rotate(angle,x,y,z)
let mutable vv = [|Vector3.UnitY*Vector3.UnitZ*Vector3.UnitX*scale|]
let mutable iv = [|0;1;2|]
//let show array = printfn "%A" array
let mutable t =int (v.UIValue(textInfo))
if t & 6 then
elif t & 0 then
for j in 0 .. t do
let mutable av = Array.create 0 Vector3.Zero
let mutable ev = Array.create 0 0
for i in 0 .. 3 .. iv.Length - 1 do
let (vvv,iiv) = v.Subdivide(vv.[iv.[i]],vv.[iv.[i+1]],vv.[iv.[i+2]])
let length = av.Length
av &- Array.append av vvv
let map = iiv |& Array.map (fun p -& p + length)
ev &- Array.append ev map
GL.VertexPointer(3,VertexPointerType.Float,0,vv)
GL.DrawElements(BeginMode.Triangles,iv.Length,DrawElementsType.UnsignedInt,iv)
GL.PopMatrix()
member v.Render =
let mutable lookat = Matrix4.LookAt(new Vector3(x,y,z),Vector3.Zero,Vector3.UnitY)
GL.MatrixMode(MatrixMode.Modelview)
GL.LoadMatrix(&lookat)
GL.Rotate(v.UIValue(textLR),0.f,1.f,0.f)
GL.Rotate(v.UIValue(textUD),1.f,0.f,0.f)
GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
GL.Color3(Color.Green)
v.CreatePane(0.f,0.f,1.f,0.f)
v.CreatePane(90.f,0.f,1.f,0.f)
//v.CreatePane(180.f,0.f,1.f,0.f)
glControl.SwapBuffers()
210 let t = new loopForm()
211 t.Show()
首先我们设定逆时针方向为正方向,分别设定正面为画布填充,反面为线填充,这样我们就能很容易知道我们生成的三角形倒底是不是正确生成的我们要的面向.
然后分别取用顶点数组和顶点数组索引功能.毕竟后面的点多,一个一个去组装没这个脑力,还没性能.
如何用三角开来模拟生成球面,方法肯定很多种,这里我们可以想象下,在坐标轴的原点就是球的原点,半径为1,被X,Y,Z轴分成八个部分,我们找到正右上的那边,与X,Y,Z轴的交点分别为x1(1,0,0),y1(0,1,0),z1(0,0,1).
然后我们把x1,y1,z1连起来画一个三角形.注意这里就有顺序了,想像一下,三个点的位置,要画正面是用逆时针方向,一算,x1,y1,z1这个方向就是,但是通常为了形象些,我们从y1上开始画,然后是前z1,然后是右x1.如代码是这样
let mutable vv = [|Vector3.UnitY*Vector3.UnitZ*Vector3.UnitX*scale|]&&&& let mutable iv = [|0;1;2|]
然后就是细分这三角形,过程就是这样,一个三角形分成四个.在for j in 0 .. t 这里,t越多,分的就越多,t是0,分一次成四个,t是1,四个再分别分成四个,就是16个.最后总的三角形就是4的t+1次方.
细分算法,大致如下,已知球面上二点,a1,a2,求在这球二点中间点ax.
已知球心在中间,得知,三个向量有如下关系,向量ax = (向量a2-向量a1)*0.5 + 向量a1.这样可以算到向量a1,那点ax就是向量a1*半径.
当一个三角形分成几个三角形,也就是三个顶点变成六个顶点,那么生成生成三角形的索引也要重新更新.具体过程用图来说明.
分的越细,球面越光滑,这样只生成了八分之一的球面,后面的如何画了,前面讲了矩阵堆栈的用法,刚好可以用在这样能在我方便生成另外几个面,v.CreatePane(90.f,0.f,1.f,0.f)这个是我们绕Y轴90度后,生成另外的一个面,为了不破坏全局坐标,我们可以在转之前调用PushMatrix记住当前矩阵,画完后再用PopMatrix回到当前矩阵.
看一下程序运行后的效果图.界面上的X,Y,Z指向人眼的位置,左右与上下指旋转方向.最下面指细分的程度,值越大,细分的越厉害.
大家可以把顶点索引变下顺序,然后再来看下效果,也可以把余下的六面全部补起.
阅读(...) 评论()下次自动登录
现在的位置:
& 综合 & 正文
OpenGL 参数方程绘制球
在OpenGL中用参数方程绘制球体
#pragma once
// Windows Header Files
#include &windows.h&
// C RunTime Header Files
#include &stdlib.h&
#include &malloc.h&
#include &memory.h&
#include &tchar.h&
#include &math.h&
Sphere.cpp
#include "Sphere.h"
#include &glut.h&
#define pi 3.1415926
#define SOLID 3000
#define WIRE
typedef int SPHERE_MODE;
typedef struct Point3f
void init(void);
void reshape(int w,int h);
void display(void);
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p);
void drawSlice(point &p1,point &p2,point &p3,point &p4,SPHERE_MODE mode);
point* getPointMatrix(GLfloat radius,GLint slices);
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode);
int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow("Sphere");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutMainLoop();
void init (void)
glClearColor (0.0, 0.0, 0.0, 0.0);
glClearDepth(1);
glShadeModel(GL_SMOOTH);
GLfloat _ambient[]={1.0,1.0,1.0,1.0};
GLfloat _diffuse[]={1.0,1.0,1.0,1.0};
GLfloat _specular[]={1.0,1.0,1.0,1.0};
GLfloat _position[]={200,200,200,0};
glLightfv(GL_LIGHT0,GL_AMBIENT,_ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,_diffuse);
glLightfv(GL_LIGHT0,GL_SPECULAR,_specular);
glLightfv(GL_LIGHT0,GL_POSITION,_position);
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
void reshape(int w, int h)
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 500, 0.0, 500, -500, 500);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void display(void)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glTranslated(250,250,0);
glRotated(30,1,0,0);
glRotated(60,0,1,0);
glRotated(90,0,0,1);
glColor3f(1.0,1.0,1.0);
drawSphere(200,20,WIRE);
glFlush();
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p)
p.x=radius*sin(a*pi/180.0)*cos(b*pi/180.0);
p.y=radius*sin(a*pi/180.0)*sin(b*pi/180.0);
p.z=radius*cos(a*pi/180.0);
void drawSlice(point &p1,point &p2,point &p3,point &p4,SPHERE_MODE mode)
switch(mode)
case SOLID:
glBegin(GL_QUADS);
case WIRE:
glBegin(GL_LINE_LOOP);
glColor3f(1,0,0);
glVertex3f(p1.x,p1.y,p1.z);
glVertex3f(p2.x,p2.y,p2.z);
glVertex3f(p3.x,p3.y,p3.z);
glVertex3f(p4.x,p4.y,p4.z);
point* getPointMatrix(GLfloat radius,GLint slices)
int i,j,w=2*slices,h=
float a=0.0,b=0.0;
float hStep=180.0/(h-1);
float wStep=360.0/w;
int length=w*h;
matrix=(point *)malloc(length*sizeof(point));
if(!matrix)return NULL;
for(a=0.0,i=0;i&h;i++,a+=hStep)
for(b=0.0,j=0;j&w;j++,b+=wStep)
getPoint(radius,a,b,matrix[i*w+j]);
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode)
int i=0,j=0,w=2*slices,h=
mx=getPointMatrix(radius,slices);
if(!mx)return 0;
for(;i&h-1;i++)
for(j=0;j&w-1;j++)
drawSlice(mx[i*w+j],mx[i*w+j+1],mx[(i+1)*w+j+1],mx[(i+1)*w+j],mode);
drawSlice(mx[i*w+j],mx[i*w],mx[(i+1)*w],mx[(i+1)*w+j],mode);
写程序不写注释,而且只用英文:这就是我的风格。所以有必要把上面的东西解析一下。
首先要声明的是如果想运行上面的程序,在你的计算机上必须装有GLUT开发工具,并要将GLUT的头文件和库文件添加到你开发环境的头文件和库文件的目录中,并且让编译器能找到GLUT的动态连接库,即GLUT32.DLL。具体请参见GLUT的安装方法……
进入正题:
先是Sphere.h,包含了windows.h头文件和一些其他可能用到的标准C头文件,可以直接写到Sphere.cpp里面。接下来在Sphere.cpp里面包含Sphere.h和GLUT的头文件glut.h
2 自定义宏、类型和结构体
#define pi 3.1415926
定义圆周率pi,后面计算坐标时要用到#define SOLID 3000
#define WIRE
定义画球的模式,SOLID表示画实面球,WIRE表示画网格球
typedef int SPHERE_MODE;
定义画球模式的类型,其实是个int整形
typedef struct Point3f
定义记录空间点坐标的结构体point,GLfloat类型其实就是GLUT里面定义的float类型
3 函数声明和实现
void init(void);
void reshape(int w,int h);
void display(void);
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p);
void drawSlice(point &p1,point &p2,point &p3,point &p4,SPHERE_MODE mode);
point* getPointMatrix(GLfloat radius,GLint slices);
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode);
init()、reshape()和display()这3个函数是OpenGL绘图的一般过程所用的函数,这3个函数的函数名可以不同,但形参部分的形参类型必须一样(注),且这3个函数在主函数中是依次调用的。
(注: void reshape(int w,int h); 也可作 void resizeWindow(int width,int height))
在init()函数里,分别对屏幕背景颜色,投影模式,光照,光原位置等进行了设置,并启用纹理,灯光和深度测试;
reshape()函数里面,对视口和投影矩阵进行设置;
接下来是display()函数,先移动原点坐标到窗口中心位置,再对所要绘制的图形进行一定角度的旋转,设置绘图的颜色,然后调用drawSphere()绘制球体。
关于init(),reshape()和display()这3个函数里面的具体过程,请参见OpenGL的相关教程。
下面的几个函数是绘制球体所用的,这里先简略给出他们的作用,具体实现将在下一部分详细解析:
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p)
根据半径radius,a角(半径与Z轴正向的夹角),b角(半径在xy平面的投影与x轴正向的夹角)算出球面上点的坐标并记录到point类型的参数p中。
void drawSlice(point &p1,point &p2,point &p3,point &p4,SPHERE_MODE mode
根据实际调用时提供的4个点的坐标在空间画四边形,并用mode参数确定所画四边形是空心还是实心的。
point* getPointMatrix(GLfloat radius,GLint slices)
根据提供的半径radius和分块数slices计算出一系列球面上的点的坐标,储存在一个动态创建的矩阵中,并返回指向该矩阵的指针,要求的分块数越多,计算的点就越多,球面就越光滑,矩阵也就越大。
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode)
绘球所调用的函数,根据提供的半径radius和分块数slices在窗体中绘制球体,分块数越大,球面越光滑,mode参数用于确定要绘制实面球还是网格球。
(继续写……)
4 绘球部分
这里绘制的球,其实是个球面。把球面看成是由很多个小的四边形平面构成的,这样就可以通过绘制这些小的四边形平面来构成整个球面。而对于两极部分,
需要用三角形将球面两端封起来,如果把这些三角型看作是有两个顶点重合的四边形,问题就得到进一步的简化。因此只需知道球面上一些列点的空间坐标,就可以
利用这些点来绘制四边形从而得到整个球面。把整个球面展开,可见球面上这些点构成的是一个矩阵,而这个矩阵中纵横相邻的四个元素的坐标正好构成的就是球面
上的一个小平面。
如何获取球面上点的坐标?
利用球的空间坐标参数方程
x=r·sin(α)·cos(β)
y=r·sin(α)·sin(β)
z=r·cos(α)
是球的半径,α角是半径与Z轴正向的夹角,β角是半径在xy平面的投影与x轴正向的夹角,他们的取值范围是
0≤β≤2π
因此函数 getPoint() 就是通过此参数方程来获得空间点的坐标;
int getPoint(GLfloat radius,GLfloat a,GLfloat b,point &p)
p.x=radius*sin(a*pi/180.0)*cos(b*pi/180.0);
p.y=radius*sin(a*pi/180.0)*sin(b*pi/180.0);
p.z=radius*cos(a*pi/180.0);
值得注意的是 sin() 和 cos() 函数的参数是弧度角,而形参传入的是角度角,因此需要进行转换
获取球面坐标矩阵
形参slices确定取点的间隔大小。
角的大小是0~180度,β角的大小是0~360度,因此我们在z轴方向,每隔180/(slice-1)取一横列的点作为α角,在垂直于z轴的平面上,
每隔360/slice取一纵列的点作为β角。由于β角的范围是α角的两倍,所以矩阵横向取点的个数是纵向取点的两倍。
接下来就是动态分配内存空间,然后通过循环为矩阵中的元素,即球面上的点的坐标赋值。这里调用 getPoint() 这个函数来计算空间点坐标。虽然分配出来的空间是线性的,但可以通过元素的下标计算确定该元素在线性空间的位置。
point* getPointMatrix(GLfloat radius,GLint slices)
int i,j,w=2*slices,h=
float a=0.0,b=0.0;
float hStep=180.0/(h-1);
float wStep=360.0/w;
int length=w*h;
matrix=(point *)malloc(length*sizeof(point));
if(!matrix)return NULL;
for(a=0.0,i=0;i&h;i++,a+=hStep)
for(b=0.0,j=0;j&w;j++,b+=wStep)
getPoint(radius,a,b,matrix[i*w+j]);
void drawSlice(point &p1,point &p2,point &p3,point &p4,SPHERE_MODE mode)
switch(mode)
case SOLID:
glBegin(GL_QUADS);
case WIRE:
glBegin(GL_LINE_LOOP);
glColor3f(1,0,0);
glVertex3f(p1.x,p1.y,p1.z);
glVertex3f(p2.x,p2.y,p2.z);
glVertex3f(p3.x,p3.y,p3.z);
glVertex3f(p4.x,p4.y,p4.z);
形参mode确定了绘图的模式,当其为SOLID时,用GL_QUADS模式绘实平面,当为WIRE时,就用
GL_LINE_LOOP绘制首尾相连的四条线。注意最后要用 glEnd() 结束对该平面的绘制,否则会得到“怪异”的结果。
int drawSphere(GLfloat radius,GLint slices,SPHERE_MODE mode)
int i=0,j=0,w=2*slices,h=
mx=getPointMatrix(radius,slices);
if(!mx)return 0;
for(;i&h-1;i++)
for(j=0;j&w-1;j++)
drawSlice(mx[i*w+j],mx[i*w+j+1],mx[(i+1)*w+j+1],mx[(i+1)*w+j],mode);
drawSlice(mx[i*w+j],mx[i*w],mx[(i+1)*w],mx[(i+1)*w+j],mode);
就是绘制球面的主框架函数了,先调用 getPointMatrix()
来产生球面点矩阵,并用局部变量mx记录该矩阵在内存中的位置。接下来就是把该矩阵上记录的点用 drawSlice()
函数绘成平面。例如mx[0,0],mx[0,1],mx[1,1],mx[1,0]这四个元素中记录的就是沿逆时针方向构成球面上第一个平面的4个点
了。要注意的是,在矩阵的横向上,最后一列点需要和第一列点连起来再绘制平面,这样产生的球面才是封闭的。在函数最后要把动态分配的内存空间释放掉,不然
会造成内存泄露。如果绘制成功,函数返回1,否则返回0。
(终于写到结尾了……)
有了上面几个函数就可以在直接调用 drawSphere() 函数来绘制不同大小,不同精度和不同模式的球了。
(全文完)
/tianshi_17th/blog/static//
&&&&推荐文章:
【上篇】【下篇】}

我要回帖

更多关于 opengl绘制球体函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信