欢迎欢迎 欢迎大家来到CG的世界

OpenGL教程Lesson: 28贝塞尔曲面

上一篇 / 下一篇  2006-12-31 10:38:26 / 个人分类:CG教程

这篇教程旨在介绍贝塞尔曲面,希望有比我更懂艺术的人能用她作出一些很COOL的东东并且展示给大家。教程不能用做一个完整的贝塞尔曲面库,而是一个展示概念的程序让你熟悉曲面怎样实现的。而且这不是一篇正规的文章,为了方便理解,我也许在有些地方术语不当;我希望大家能适应这个。最后,对那些已经熟悉贝塞尔曲面想看我写的如何的,真是丢脸;-)但你要是找到任何纰漏让我或者NeHe知道,毕竟人无完人嘛?还有,所有代码没有象我一般写程序那样做优化,这是故意的。我想每个人都能明白写的是什么。好,我想介绍到此为止,继续看下文!

This tutorial is intended to introduce you to Bezier Surfaces in the hopes that someone more artistic than myself will do something really cool with them and show all of us. This is not intended as a complete Bezier patch library, but more as proof of concept code to get you familiar with how these curved surfaces actually work. Also, as this is a very informal piece, I may have occasional lapses in correct terminology in favor of comprehensability; I hope this sits well with everyone. Finally, to those of you already familiar with Beziers who are just reading this to see if I screw up, shame on you ;-), but if you find anything wrong by all means let me or NeHe know, after all no one’s perfect, eh? Oh, and one more thing, none of this code is optimized beyond my normal programming technique, this is by design. I want everyone to be able to see exactly what is going on. Well, I guess that’s enough of an intro. On with the show!
 
数学::恶魔之音::(警告:内容有点长~)
The Math - ::evil music:: (warning, kinda long section)
 
好,如果想理解贝塞尔曲面没有对其数学基本的认识是很难的,如果你不愿意读这一部分或者你已经知道了关于她的数学知识你可以跳过。首先我会描述贝塞尔曲线再介绍生成贝塞尔曲面。
奇怪的是,如果你用过一个图形程序,你就已经熟悉了贝塞尔曲线,也许你接触的是另外的名称。它们是画曲线的最基本的方法,而且通常被表示成一系列点,其中有两个点与两端点表示左右两端的切线。下图展示了一个例子。

Ok, it will be very hard to understand Beziers without at least a basic understanding of the math behind it, however, if you just don’t feel like reading this section or already know the math, you can skip it. First I will start out by describing the Bezier curve itself then move on to how to create a Bezier Patch.

Odds are, if you’ve ever used a graphics program you are already familiar with Bezier curves, perhaps not by that name though. They are the primary method of drawing curved lines and are commonly represented as a series of points each with 2 points representing the tangent at that point from the left and right. Here’s what one looks like:



 
这是最基础的贝塞尔曲线(长点的由很多点在一起(多到你都没发现))。这个曲线由4个点定义,有2个端点和2个中间控制点。对计算机而言这些点都是一样的,但是特意的我们通常把前后两对点分别连接,因为他们的连线与短点相切。曲线是一个参数化曲线,画的时候从曲线上平均找几点连接。这样你可以控制曲线曲面的精度(和计算量)。最通常的方法是远距离少细分近距离多细分,对视点,看上去总是很完好的曲面而对速度的影响总是最小。
贝塞尔曲面基于一个基本方程,其他复杂的都是基于此。方程为:

This is the most basic Bezier curve possible (longer ones are made by attaching many of these together (many times without the user realizing it)). This curve is actually defined by only 4 points, those would be the 2 ending control points and the 2 middle control points. To the computer, all the points are the same, but to aid in design we often connect the first and the last two, respectively, because those lines will always be tangent to the endpoint. The curve is a parametric curve and is drawn by finding any number of points evenly spaced along the curve and connecting them with straight lines. In this way you can control the resolution of the patch (and the amount of computation). The most common way to use this is to tesselate it less at a farther distance and more at a closer distance so that, to the viewer, it always appears to be a perfectly curved surface with the lowest possible speed hit.

Bezier curves are based on a basis function from which more complicated versions are derived. Here’s the function:

t + (1 - t) = 1
 
 
看起来很简单不是?的确是的,这是最基本的贝塞尔曲线,一个一维的曲线。你也许从术语中猜到,贝塞尔曲线是多项式形式的。从线性代数知,一个一维的多项式是一条直线,没多大意思。好,因为基本方程对所有t都成立,我们可以平方,立方两边,怎么都行,等式都是成立的,对吧?好,我们试试立方。

Sounds simple enough huh? Well it really is, this is the Bezier most basic Bezier curve, a 1st degree curve. As you may have guessed from the terminology, the Bezier curves are polynomials, and as we remember from algebra, a 1st degree polynomial is just a straight line; not very interesting. Well, since the basis function is true for all numbers t, we can square, cube, whatever, each side and it will still be true right? Well, lets try cubing it.

(t + (1-t))^3 = 1^3

t^3 + 3*t^2*(1-t) + 3*t*(1-t)^2 + (1-t)^3 = 1
 
 
这是我们最常用的计算贝塞尔曲面的方程,a)她是最低维的不需要在一个平面内的多项式(有4个控制点),而且b)两边的切线互相没有联系(对于2维的只有3个控制点)。那么你看到了贝塞尔曲线了吗?呵呵,我们都没有,因为我还要加一个东西。
好,因为方程左边等于1,可以肯定如果你把所有项加起来还是等于1。这是否意味着在计算曲线上一点时可以以此决定该用每个控制点的多少呢?(答案是肯定的)你对了!当我们要计算曲线上一点的值我们只需要用控制点(表示为向量)乘以每部分再加起来。基本上我们要用0<=t<=1,但不是必要的。不明白了把?这里有函数:

This is the equation we use to calculate the most common Bezier, the 3rd degree Bezier curve. This is most common for two reasons, a) it’s the lowest degree polynomial that need not necesarily lie in a plane (there are 4 control points) and b) the tangent lines on the sides are not dependant on one another (with a 2nd degree there would be only 3 control points). So do you see the Bezier curve yet? Hehe, me neither, that’s because I still need to add one thing.

Ok, since the entire left side is equal to 1, it’s safe to assume that if you add all the components they should still equal one. Does this sound like it could be used to decide how much of each control point to use in calculating a point on the curve? (hint: just say yes ;-) ) Well you’re right! When we want to calculate the value of a point some percent along the curve we simply multiply each part by a control point (as a vector) and find the sum. Generally, we’ll work with 0 <= t <= 1, but it’s not technically necessary. Confused yet? Here’s the function:

P1*t^3 + P2*3*t^2*(1-t) + P3*3*t*(1-t)^2 + P4*(1-t)^3 = Pnew
 
 
因为多项式是连续的,有一个很好的办法在4个点间插值。曲线仅经过P1,P4,分别当t=1,0。
好,一切都很好,但现在我怎么把这个用在3D里呢?其实很简单,为了做一个贝塞尔曲面,你需要16个控制点,(4*4),和2个变量t,v。你要做的是计算在分量v的沿4条平行曲线的点,再用这4个点计算在分量t的点。计算了足够的这些点,我们可以用三角带连接他们,画出贝塞尔曲面。

Because polynomials are always continuous, this makes for a good way to morp between the 4 points. The only points it actually reaches though are P1 and P4, when t = 1 and 0 respectively.

Now, that’s all well and good, but how can I use these in 3D you ask? Well it’s actually quite simple, in order to form a Bezier patch, you need 16 control points (4*4), and 2 variables t and v. What you do from there is calculate a point at v along 4 of the parallel curves then use those 4 points to make a new curve and calculate t along that curve. By calculating enough of these points, we can draw triangle strips to connect them, thus drawing the Bezier patch.
 


 
恩,我认为现在已经有足够的数学背景了,看代码把!

Well, I suppose that’s enough math for now, on to the code!
#include <windows.h>// Header File For Windows#include <math.h>// Header File For Math Library Routines#include <stdio.h>// Header File For Standard I/O Routines#include <stdlib.h>// Header File For Standard Library#include <gl\gl.h>// Header File For The OpenGL32 Library#include <gl\glu.h>// Header File For The GLu32 Library#include <gl\glaux.h>// Header File For The Glaux Librarytypedef struct point_3d {// Structure For A 3-Dimensional Point ( NEW )double x, y, z;
} POINT_3D;

typedef struct bpatch {// Structure For A 3rd Degree Bezier Patch ( NEW )POINT_3D	anchors[4][4];// 4x4 Grid Of Anchor PointsGLuint		dlBPatch;// Display List For Bezier PatchGLuint		texture;// Texture For The Patch} BEZIER_PATCH;

HDC			hDC=NULL;// Private GDI Device ContextHGLRC			hRC=NULL;// Permanent Rendering ContextHWND			hWnd=NULL;// Holds Our Window HandleHINSTANCE		hInstance;// Holds The Instance Of The ApplicationDEVMODE			DMsaved;// Saves The Previous Screen Settings ( NEW )bool			keys[256];// Array Used For The Keyboard Routinebool			active=TRUE;// Window Active Flag Set To TRUE By Defaultbool			fullscreen=TRUE;// Fullscreen Flag Set To Fullscreen Mode By DefaultGLfloat			rotz = 0.0f;// Rotation About The Z AxisBEZIER_PATCH		mybezier;// The Bezier Patch We’re Going To Use ( NEW )BOOL			showCPoints=TRUE;// Toggles Displaying The Control Point Grid ( NEW )int			divs = 7;// Number Of Intrapolations (Controls Poly Resolution) ( NEW )LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);// Declaration For WndProc
以下是一些简单的向量数学的函数。如果你是C++爱好者你可以用一个顶点类(保证其为3D的)。
The following are just a few quick functions for some simple vector math. If you’re a fan of C++ you might consider using a point class (just make sure it’s 3d).
// Adds 2 Points. Don’t Just Use ’+’ ;)POINT_3D pointAdd(POINT_3D p, POINT_3D q) {
	p.x += q.x;		p.y += q.y;		p.z += q.z;
	return p;
}// Multiplies A Point And A Constant. Don’t Just Use ’*’POINT_3D pointTimes(double c, POINT_3D p) {
	p.x *= c;	p.y *= c;	p.z *= c;
	return p;
}// Function For Quick Point CreationPOINT_3D makePoint(double a, double b, double c) {
	POINT_3D p;
	p.x = a;	p.y = b;	p.z = c;
	return p;
}
这基本上是用C写的3维的基本函数,她用变量u和4个顶点的数组计算曲线上点。每次给u加上一定值,从0到1,我们可得一个很好的近似曲线。
This is basically just the 3rd degree basis function written in C, it takes a variable u and an array of 4 points and computes a point on the curve. By stepping u in equal increments between 0 and 1, we’ll get a nice approximation of the curve.
// Calculates 3rd Degree Polynomial Based On Array Of 4 Points// And A Single Variable (u) Which Is Generally Between 0 And 1POINT_3D Bernstein(float u, POINT_3D *p) {
	POINT_3D	a, b, c, d, r;

	a = pointTimes(pow(u,3), p[0]);
	b = pointTimes(3*pow(u,2)*(1-u), p[1]);
	c = pointTimes(3*u*pow((1-u),2), p[2]);
	d = pointTimes(pow((1-u),3), p[3]);

	r = pointAdd(pointAdd(a, b), pointAdd(c, d));

	return r;
}
这个函数完成共享工作,生成所有三角带,保存在display list。我们这样就不需要每贞都重新计算曲面,除了当其改变时。另外,你可能想用一个很酷的效果,用MORPHING教程改变控制点位置。这可以做一个很光滑,有机的,morphing效果,只要一点点开销(你只要改变16个点,但要从新计算)。“最后”的数组元素用来保存前一行点,(因为三角带需要两行)。而且,纹理坐标由表示百分比的u,v来计算(平面映射)。
还有一个我们没做的是计算法向量做光照。到了这一步,你基本上有2种选择。第一是找每个三角形的中心计算X,Y轴的切线,再做叉积得到垂直与两向量的向量,再归一化,得到法向量。或者(恩,这是更好的方法)你可以直接用三角形的法矢(用你最喜欢的方法计算)得到一个近似值。我喜欢后者;我认为不值得为了一点点真实感影响速度。
This function does the lion’s share of the work by generating all the triangle strips and storing them in a display list. We do this so that we don’t have to recalculate the patch each frame, only when it changes. By the way, a cool effect you might want to try might be to use the morphing tutorial to morph the patch’s control points. This would yield a very cool smooth, organic, morphing effect for relatively little overhead (you only morph 16 points, but you have to recalculate). The "last" array is used to keep the previous line of points (since a triangle strip needs both rows). Also, texture coordinates are calculated by using the u and v values as the percentages (planar mapping).

One thing we don’t do is calculate the normals for lighting. When it comes to this, you basically have two options. The first is to find the center of each triangle, then use a bit of calculus and calculate the tangent on both the x and y axes, then do the cross product to get a vector perpendicular to both, THEN normalize the vector and use that as the normal. OR (yes, there is a faster way) you can cheat and just use the normal of the triangle (calculated your favorite way) to get a pretty good approximation. I prefer the latter; the speed hit, in my opinion, isn’t worth the extra little bit of realism.
// Generates A Display List Based On The Data In The Patch// And The Number Of DivisionsGLuint genBezier(BEZIER_PATCH patch, int divs) {
	int		u = 0, v;
	float		py, px, pyold; 
	GLuint		drawlist = glGenLists(1);// Make The Display ListPOINT_3D	temp[4];
	POINT_3D	*last = (POINT_3D*)malloc(sizeof(POINT_3D)*(divs+1));// Array Of Points To Mark The First Line Of Polysif (patch.dlBPatch != NULL)// Get Rid Of Any Old Display ListsglDeleteLists(patch.dlBPatch, 1);

	temp[0] = patch.anchors[0][3];// The First Derived Curve (Along X-Axis)temp[1] = patch.anchors[1][3];
	temp[2] = patch.anchors[2][3];
	temp[3] = patch.anchors[3][3];

	for (v=0;v<=divs;v++) {// Create The First Line Of Pointspx = ((float)v)/((float)divs);// Percent Along Y-Axis// Use The 4 Points From The Derived Curve To Calculate The Points Along That Curvelast[v] = Bernstein(px, temp);
	}

	glNewList(drawlist, GL_COMPILE);// Start A New Display ListglBindTexture(GL_TEXTURE_2D, patch.texture);// Bind The Texturefor (u=1;u<=divs;u++) {
		py    = ((float)u)/((float)divs);// Percent Along Y-Axispyold = ((float)u-1.0f)/((float)divs);// Percent Along Old Y Axistemp[0] = Bernstein(py, patch.anchors[0]);// Calculate New Bezier Pointstemp[1] = Bernstein(py, patch.anchors[1]);
		temp[2] = Bernstein(py, patch.anchors[2]);
		temp[3] = Bernstein(py, patch.anchors[3]);

		glBegin(GL_TRIANGLE_STRIP);// Begin A New Triangle Stripfor (v=0;v<=divs;v++) {
			px = ((float)v)/((float)divs);// Percent Along The X-AxisglTexCoord2f(pyold, px);// Apply The Old Texture CoordsglVertex3d(last[v].x, last[v].y, last[v].z);// Old Pointlast[v] = Bernstein(px, temp);// Generate New PointglTexCoord2f(py, px);// Apply The New Texture CoordsglVertex3d(last[v].x, last[v].y, last[v].z);// New Point}

		glEnd();// END The Triangle Strip}
	
	glEndList();// END The Listfree(last);// Free The Old Vertices Arrayreturn drawlist;// Return The Display List}
这里我们调用一个我认为有一些很酷的值的矩阵。可?运嬉飧恼庑┲悼纯幢涑墒裁囱#?-)
Here we’re just loading the matrix with some values I’ve picked that I think look cool. Feel free to screw around with these and see what it looks like. :-)
void initBezier(void) {	
	mybezier.anchors[0][0] = makePoint(-0.75,	-0.75,	-0.50);// Set The Bezier Verticesmybezier.anchors[0][1] = makePoint(-0.25,	-0.75,	 0.00);
	mybezier.anchors[0][2] = makePoint( 0.25,	-0.75,	 0.00);
	mybezier.anchors[0][3] = makePoint( 0.75,	-0.75,	-0.50);
	mybezier.anchors[1][0] = makePoint(-0.75,	-0.25,	-0.75);
	mybezier.anchors[1][1] = makePoint(-0.25,	-0.25,	 0.50);
	mybezier.anchors[1][2] = makePoint( 0.25,	-0.25,	 0.50);
	mybezier.anchors[1][3] = makePoint( 0.75,	-0.25,	-0.75);
	mybezier.anchors[2][0] = makePoint(-0.75,	 0.25,	 0.00);
	mybezier.anchors[2][1] = makePoint(-0.25,	 0.25,	-0.50);
	mybezier.anchors[2][2] = makePoint( 0.25,	 0.25,	-0.50);
	mybezier.anchors[2][3] = makePoint( 0.75,	 0.25,	 0.00);
	mybezier.anchors[3][0] = makePoint(-0.75,	 0.75,	-0.50);
	mybezier.anchors[3][1] = makePoint(-0.25,	 0.75,	-1.00);
	mybezier.anchors[3][2] = makePoint( 0.25,	 0.75,	-1.00);
	mybezier.anchors[3][3] = makePoint( 0.75,	 0.75,	-0.50);
	mybezier.dlBPatch = NULL;// Go Ahead And Initialize This To NULL}
这是一个优化的调位图的函数。可以很简单的把他们放进一个简单循环里调一组。
This is basically just an optimized routine to load a single bitmap. It can easily be used to load an array of em just by putting it in a simple loop.
// Load Bitmaps And Convert To TexturesBOOL LoadGLTexture(GLuint *texPntr, char* name)
{
	BOOL success = FALSE;
	AUX_RGBImageRec *TextureImage = NULL;

	glGenTextures(1, texPntr);// Generate 1 TextureFILE* test=NULL;
	TextureImage = NULL;

	test = fopen(name, "r");// Test To See If The File Existsif (test != NULL) {// If It Doesfclose(test);// Close The FileTextureImage = auxDIBImageLoad(name);// And Load The Texture}

	if (TextureImage != NULL) {// If It Loadedsuccess = TRUE;// Typical Texture Generation Using Data From The BitmapglBindTexture(GL_TEXTURE_2D, *texPntr);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	}

	if (TextureImage->data)
		free(TextureImage->data);

	return success;
}
仅仅加了曲面初始化在这。你每次建一个曲面时都会用这个。再一次,这里是一个用C++的好地方(贝塞尔曲面类?)。
Just adding the patch initialization here. You would do this whenever you create a patch. Again, this might be a cool place to use C++ (bezier class?).
int InitGL(GLvoid)// All Setup For OpenGL Goes Here{
	glEnable(GL_TEXTURE_2D);// Enable Texture MappingglShadeModel(GL_SMOOTH);// Enable Smooth ShadingglClearColor(0.05f, 0.05f, 0.05f, 0.5f);// Black BackgroundglClearDepth(1.0f);// Depth Buffer SetupglEnable(GL_DEPTH_TEST);// Enables Depth TestingglDepthFunc(GL_LEQUAL);// The Type Of Depth Testing To DoglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Really Nice Perspective CalculationsinitBezier();// Initialize the Bezier’s Control Grid ( NEW )LoadGLTexture(&(mybezier.texture), "./Data/NeHe.bmp");// Load The Texture ( NEW )mybezier.dlBPatch = genBezier(mybezier, divs);// Generate The Patch ( NEW )return TRUE;// Initialization Went OK}
首先调贝塞尔display list。再(如果边线要画)画连接控制点的线。你可以用SPACE键开关这个。
First call the bezier’s display list. Then (if the outlines are on) draw the lines connecting the control points. You can toggle these by pressing SPACE.
int DrawGLScene(GLvoid)	{// Here’s Where We Do All The Drawingint i, j;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear Screen And Depth BufferglLoadIdentity();// Reset The Current Modelview MatrixglTranslatef(0.0f,0.0f,-4.0f);// Move Left 1.5 Units And Into The Screen 6.0glRotatef(-75.0f,1.0f,0.0f,0.0f);
	glRotatef(rotz,0.0f,0.0f,1.0f);// Rotate The Triangle On The Z-AxisglCallList(mybezier.dlBPatch);// Call The Bezier’s Display List// This Need Only Be Updated When The Patch Changesif (showCPoints) {// If Drawing The Grid Is Toggled OnglDisable(GL_TEXTURE_2D);
		glColor3f(1.0f,0.0f,0.0f);
		for(i=0;i<4;i++) {// Draw The Horizontal LinesglBegin(GL_LINE_STRIP);
			for(j=0;j<4;j++)
				glVertex3d(mybezier.anchors[i][j].x, mybezier.anchors[i][j].y, mybezier.anchors[i][j].z);
			glEnd();
		}
		for(i=0;i<4;i++) {// Draw The Vertical LinesglBegin(GL_LINE_STRIP);
			for(j=0;j<4;j++)
				glVertex3d(mybezier.anchors[j][i].x, mybezier.anchors[j][i].y, mybezier.anchors[j][i].z);
			glEnd();
		}
		glColor3f(1.0f,1.0f,1.0f);
		glEnable(GL_TEXTURE_2D);
	}

	return TRUE;// Keep Going}
这个函数有一些改了的代码可帮你让你的程序更具适应性。她与贝塞尔曲面无关,但是她对一些显卡修正了在全屏改变分辨率的一个错误。(象我的卡,一个老的垃圾ATI Rage PRO和一些其他的)。我希望你以后可以用她以便我和一些同样有类似卡的可以看到你的COOL程序的正确代码。要作些更改,改一下KillGLWindow(),要定义DMsaved,而且要改CreateGLWindow()的一行代码(标记了的)。
This function contains some modified code to make your projects more compatable. It doesn’t have anything to do with Bezier curves, but it does fix a problem with switching back the resolution after fullscreen mode with some video cards (including mine, a crappy old ATI Rage PRO, and a few others). I hope, you’ll use this from now on so me and others with similar cards can view your cool examples GL code properly. To make these modifications make the changes in KillGLWindow(), make sure and define DMsaved, and make the one line change in CreateGLWindow() (it’s marked).
GLvoid KillGLWindow(GLvoid)// Properly Kill The Window{
	if (fullscreen)// Are We In Fullscreen Mode?{
		if (!ChangeDisplaySettings(NULL,CDS_TEST)) {// If The Shortcut Doesn’t Work ( NEW )ChangeDisplaySettings(NULL,CDS_RESET);// Do It Anyway (To Get The Values Out Of The Registry) ( NEW )ChangeDisplaySettings(&DMsaved,CDS_RESET);// Change It To The Saved Settings ( NEW )} else {
			ChangeDisplaySettings(NULL,CDS_RESET);// If It Works, Go Right Ahead ( NEW )}
			
		ShowCursor(TRUE);// Show Mouse Pointer}

	if (hRC)// Do We Have A Rendering Context?{
		if (!wglMakeCurrent(NULL,NULL))// Are We Able To Release The DC And RC Contexts?{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))// Are We Able To Delete The RC?{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;// Set RC To NULL}

	if (hDC && !ReleaseDC(hWnd,hDC))// Are We Able To Release The DC{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;// Set DC To NULL}

	if (hWnd && !DestroyWindow(hWnd))// Are We Able To Destroy The Window?{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;// Set hWnd To NULL}

	if (!UnregisterClass("OpenGL",hInstance))// Are We Able To Unregister Class{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;// Set hInstance To NULL}
}
只要在这加上EnumDisplaySettings()命令保存老的显示设置。(对老显卡的部分修正)。
Just added the EnumDisplaySettings() command here to save the old display settings. (part of the old graphics card fix).
// This Code Creates Our OpenGL Window.  Parameters Are:			*// title		- Title To Appear At The Top Of The Window		*// width		- Width Of The GL Window Or Fullscreen Mode		*// height		- Height Of The GL Window Or Fullscreen Mode		*// bits			- Number Of Bits To Use For Color (8/16/24/32)		*// fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;// Holds The Results After Searching For A MatchWNDCLASS	wc;// Windows Class StructureDWORD		dwExStyle;// Window Extended StyleDWORD		dwStyle;// Window StyleRECT		WindowRect;// Grabs Rectangle Upper Left / Lower Right ValuesWindowRect.left=(long)0;// Set Left Value To 0WindowRect.right=(long)width;// Set Right Value To Requested WidthWindowRect.top=(long)0;// Set Top Value To 0WindowRect.bottom=(long)height;// Set Bottom Value To Requested Heightfullscreen=fullscreenflag;// Set The Global Fullscreen FlaghInstance		= GetModuleHandle(NULL);// Grab An Instance For Our Windowwc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;// Redraw On Size, And Own DC For Windowwc.lpfnWndProc		= (WNDPROC) WndProc;// WndProc Handles Messageswc.cbClsExtra		= 0;// No Extra Window Datawc.cbWndExtra		= 0;// No Extra Window Datawc.hInstance		= hInstance;// Set The Instancewc.hIcon		= LoadIcon(NULL, IDI_WINLOGO);// Load The Default Iconwc.hCursor		= LoadCursor(NULL, IDC_ARROW);// Load The Arrow Pointerwc.hbrBackground	= NULL;// No Background Required For GLwc.lpszMenuName		= NULL;// We Don’t Want A Menuwc.lpszClassName	= "OpenGL";// Set The Class NameEnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DMsaved);// Save The Current Display State ( NEW )if (fullscreen)// Attempt Fullscreen Mode?{
		DEVMODE dmScreenSettings;// Device Modememset(&dmScreenSettings,0,sizeof(dmScreenSettings));// Makes Sure Memory’s CleareddmScreenSettings.dmSize=sizeof(dmScreenSettings);// Size Of The Devmode StructuredmScreenSettings.dmPelsWidth	= width;// Selected Screen WidthdmScreenSettings.dmPelsHeight	= height;// Selected Screen HeightdmScreenSettings.dmBitsPerPel	= bits;// Selected Bits Per PixeldmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

	... Code Cut To Save Space (No Further Changes To This Function) ...

	return TRUE;// Success}
我在这里加了旋转曲面的代码,增加/降低分辨率,显示与否控制点连线。
All I did here was add commands to rotate the patch, raise/lower the resolution, and toggle the control lines.
int WINAPI WinMain(	HINSTANCE	hInstance,// InstanceHINSTANCE	hPrevInstance,// Previous InstanceLPSTR		lpCmdLine,// Command Line Parametersint		nCmdShow)// Window Show State{
	MSG		msg;// Windows Message StructureBOOL	done=FALSE;// Bool Variable To Exit Loop// Ask The User Which Screen Mode They Preferif (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
		fullscreen=FALSE;// Windowed Mode}// Create Our OpenGL Windowif (!CreateGLWindow("NeHe’s Solid Object Tutorial",640,480,16,fullscreen))
	{
		return 0;// Quit If Window Was Not Created}

	while(!done)// Loop That Runs While done=FALSE{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))// Is There A Message Waiting?{
			if (msg.message==WM_QUIT)// Have We Received A Quit Message?{
				done=TRUE;// If So done=TRUE}
			else// If Not, Deal With Window Messages{
				TranslateMessage(&msg);// Translate The MessageDispatchMessage(&msg);// Dispatch The Message}
		}
		else// If There Are No Messages{// Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()if ((active && !DrawGLScene()) || keys[VK_ESCAPE])// Active?  Was There A Quit Received?{
				done=TRUE;// ESC or DrawGLScene Signalled A Quit}
			else// Not Time To Quit, Update Screen{
				SwapBuffers(hDC);// Swap Buffers (Double Buffering)}


			if (keys[VK_LEFT])	rotz -= 0.8f;// Rotate Left ( NEW )if (keys[VK_RIGHT])	rotz += 0.8f;// Rotate Right ( NEW )if (keys[VK_UP]) {// Resolution Up ( NEW )divs++;
				mybezier.dlBPatch = genBezier(mybezier, divs);// Update The Patchkeys[VK_UP] = FALSE;
			}
			if (keys[VK_DOWN] && divs > 1) {// Resolution Down ( NEW )divs--;
				mybezier.dlBPatch = genBezier(mybezier, divs);// Update The Patchkeys[VK_DOWN] = FALSE;
			}
			if (keys[VK_SPACE]) {// SPACE Toggles showCPoints ( NEW )showCPoints = !showCPoints;
				keys[VK_SPACE] = FALSE;
			}


			if (keys[VK_F1])// Is F1 Being Pressed?{
				keys[VK_F1]=FALSE;// If So Make Key FALSEKillGLWindow();// Kill Our Current Windowfullscreen=!fullscreen;// Toggle Fullscreen / Windowed Mode// Recreate Our OpenGL Windowif (!CreateGLWindow("NeHe’s Solid Object Tutorial",640,480,16,fullscreen))
				{
					return 0;// Quit If Window Was Not Created}
			}
		}
	}// ShutdownKillGLWindow();// Kill The Windowreturn (msg.wParam);// Exit The Program}
恩,我希望这个教程让你了然于心而且你现在象我一样喜欢上了贝塞尔曲面。;-)如果你喜欢这个教程我会继续写一篇关于NURBS的如果有人喜欢。请EMAIL我让我知道你怎么想这篇教程。
关于作者:David Nikdel现在18岁在Bartow Senior High School。他的现在的项目包括研究3D曲面,一个以OPENGL为基础的游戏名叫Blazing Sands and being lazy。他的好包括编程,足球,paintballing.他明年将(希望)成为Georgia Tech新生。
Well, I hope this tutorial has been enlightening and you all now love Bezier curves as much as I do ;-). If you like this tutorial I may write another one on NURBS curves if anyone’s interested. Please e-mail me and let me know what you thought of this tutorial.

TAG: OpenGL CG教程

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

Open Toolbar