OpenGL 游戏开发: 熟悉练习【《OpenGL超级宝典》第二章】

学习了《OpenGL超级宝典》第二章后,编写了我们的第一个OpenGL程序,也真正开始了OpenGL之旅。

下面来让我们一起重新练习熟悉一下第二章的3个例子吧!

1.  "Triangle" 第一个三角形

通过这个例子我们熟悉了OpenGL的一些流程,如初始化、窗口的创建、窗口的标题、图形的渲染等。

具体的例子代码我就不复制贴出了哈~,下面我只贴出我对此例子做的一些学习扩展:

// +++++++++++++++++++++++++++++++++++++++
// 《OpenGL 超级宝典》 Chapter02 Triangle
// ---------------------------------------

// 头文件引用
......

// GLTools 封装的顶点批次处理类
GLBatch triangleBatch;
// GLTools 封装的着色器管理器
GLShaderManager shaderManager;

// +++++++++++
// 自己扩展部分
GLBatch triangle2Batch;
GLBatch triangle3Batch;
GLBatch triangle4Batch;
// -----------

// 初始化
void SetupRC()
{
    // 书本相同
	// 蓝色背景
	glClearColor(0.0f, 0.0f, 1.0f, 1.0f);

	// 初始化 GLTools  的着色器管理器
	shaderManager.InitializeStockShaders();

	// 创建一个三角形顶点数组
	GLfloat vVerts[] = {
		0.5f,  -0.5f, 0.0f,
		-0.5f, -0.5f, 0.0f,
		0.0f,  0.0f, 0.0f,
	};

	// 拷贝三角形顶点数组到 GLTools 的顶点批次中
	// GL_TRIANGLES 表示为三角形顶点
	triangleBatch.Begin(GL_TRIANGLES, 3);
	triangleBatch.CopyVertexData3f(vVerts);
	triangleBatch.End();

	// +++++++++++
	// 自己扩展部分

	// 绿色三角形顶点
	GLfloat vVerts2[] = {
		0.5f,  -0.5f, 0.0f,
		0.5f,  0.5f, 0.0f,
		0.0f,  0.0f, 0.0f,
	};
	triangle2Batch.Begin(GL_TRIANGLES, 3);
	triangle2Batch.CopyVertexData3f(vVerts2);
	triangle2Batch.End();

	// 黄色三角形顶点
	GLfloat vVerts3[] = {
		0.5f,  0.5f, 0.0f,
		-0.5f,  0.5f, 0.0f,
		0.0f,  0.0f, 0.0f,
	};
	triangle3Batch.Begin(GL_TRIANGLES, 3);
	triangle3Batch.CopyVertexData3f(vVerts3);
	triangle3Batch.End();

	// 洋红色三角形顶点
	GLfloat vVerts4[] = {
		-0.5f,  0.5f, 0.0f,
		-0.5f,  -0.5f, 0.0f,
		0.0f,  0.0f, 0.0f,
	};
	triangle4Batch.Begin(GL_TRIANGLES, 3);
	triangle4Batch.CopyVertexData3f(vVerts4);
	triangle4Batch.End();
	// -----------
}

// 渲染场景
void RenderScene(void)
{
        // 书本相同
	// 清除 颜色 和 深度 和 模板 的缓冲区数据
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	// 红色
	GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
	// 装载红色着色器
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
	// 绘制三角形
	triangleBatch.Draw();

	// +++++++++++
	// 自己扩展部分
	// 绿色
	GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
	// 装载红色着色器
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
	// 绘制三角形
	triangle2Batch.Draw();

	// 黄色
	GLfloat vYellow[] = { 1.0f, 1.0f, 0.0f, 1.0f };
	// 装载红色着色器
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vYellow);
	// 绘制三角形
	triangle3Batch.Draw();

	// 洋红色
	GLfloat vMagenta[] = { 1.0f, 0.0f, 1.0f, 1.0f };
	// 装载红色着色器
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vMagenta);
	// 绘制三角形
	triangle4Batch.Draw();
	// -----------


	// 将后台缓冲区内容切换到前台缓冲区
	glutSwapBuffers();
}

int main(int argc, char * argv[])
{
        // 书本相同
	.....
	return 0;
}

首先贴出运行的截图:

也就是通过四个三角形进行拼出一个正方形

下面进行讲解上面的程序:

1. 定义其他3个新的三角形GLBatch用于存放绘制是三角形顶点数据。

2. 定义其他3个新的三角形顶点数组,然后放入对应GLBatch中。

3. 渲染中进行渲染其他3个新的三角形。

2.  "Move" 通过按键输入让图形变得有活力

同样也只贴出扩展的内容部分:

 +++++++++++++++++++++++++++++++++++++++
 《OpenGL 超级宝典》 Chapter02 Move
 ---------------------------------------

.......

 +++++++++++
 自己扩展部分
GLBatch triangleBatch;

GLfloat fPi = 3.1415926f;
GLfloat fRad = fPi / 180.0f;
GLfloat fTriangleX = 0.0f, fTriangleY = 0.0f;
GLfloat fCurAngle = 0.0f;
// 创建一个三角形顶点数组
GLfloat fScale = 0.8f;
GLfloat vTriangleVerts[] = {
	-blockSize * fScale, 0.0f, 0.0f,
	blockSize * fScale,  0.0f, 0.0f,
	0.0f,  blockSize * fScale, 0.0f,
};

void Rotate(GLfloat angle, GLfloat *pX, GLfloat *pY)
{
	*pX = fTriangleX + (blockSize * fScale) * cos(angle * fRad);
	*pY = fTriangleY + (blockSize * fScale) * sin(angle * fRad);
}

void RotateTriangleArrow(GLfloat angle)
{
	Rotate(angle, &vTriangleVerts[0], &vTriangleVerts[1]);
	Rotate(angle + 90.0f, &vTriangleVerts[3], &vTriangleVerts[4]);
	Rotate(angle + 180.0f, &vTriangleVerts[6], &vTriangleVerts[7]);
}
// -----------

void SetupRC()
{
	.......

	triangleBatch.Begin(GL_TRIANGLES, 3);
	triangleBatch.CopyVertexData3f(vTriangleVerts);
	triangleBatch.End();
}

void SpecialKeys(int key, int x, int y)
{
	.......

	if (key == GLUT_KEY_UP) {
		fCurAngle = 0.0f;
		blockY += stepSize;
	}

	if (key == GLUT_KEY_DOWN) {
		fCurAngle = 180.0f;
		blockY -= stepSize;
	}

	if (key == GLUT_KEY_LEFT) {
		fCurAngle = 90.0f;
		blockX -= stepSize;
	}

	if (key == GLUT_KEY_RIGHT) {
		fCurAngle = 270.0f;
		blockX += stepSize;
	}

	.......

	RotateTriangleArrow(fCurAngle);
	triangleBatch.CopyVertexData3f(vTriangleVerts);

	glutPostRedisplay();
}

void RenderScene(void)
{
	......

	// 南瓜橙色
	GLfloat vPumpkinOrange[] = { 0.98f, 0.625f, 0.12f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vPumpkinOrange);
	triangleBatch.Draw();

	glutSwapBuffers();
}

int main(int argc, char * argv[])
{
	......
	return 0;
}

首先贴出运行的截图:

分别对应按下了 ↑←↓→按键后箭头的变化

下面进行讲解上面的程序:

1. 首先是定义一个三角形的GLBatch。

2. 定义一个三角形的顶点数组,用于存放三角形的顶点数据。

3. fTriangleX 和 fTriangleY 表示为 三角形底边的中心点的坐标,当按键发生变化后,通过这个原点进行旋转。

4. fPi 和 fRad 为圆周率和弧度,同样为旋转用到的参数。

5. fCurAngle 表示为旋转的角度,分别对于按键改变后进行旋转的角度。

6. fScale 为对三角形的一个缩放比例。

7. Rotate 函数用于对顶点坐标进行旋转(详细旋转的部分,在以前的有讲过,如需单独详细讲解请给我留言吧,此处就略过了)

8. RotateTriangleArrow函数用于旋转三角形的三个顶点。

9. 在SetupRC是时候将我们的三角形顶点数据进行放入我们定义的GLBatch中,然后在RenderScene的时候同样进行渲染。

10. 在SpecialKeys的时候,进行改变我们的fCurAngle(三角形旋转的角度)。

3. "Bounce" 简单的动画片,通过不断刷新进行更新渲染

通过参照 "Bounce" 和 "Triangle" 来进行做一些有意思的事情吧。

下面贴出扩展部分:

 +++++++++++++++++++++++++++++++++++++++
 《OpenGL 超级宝典》 Chapter02 Bounce
 ---------------------------------------

.......

GLShaderManager shaderManager;

GLBatch leftFootTriangleBatch;
GLBatch leftShankTriangleBatch;
GLBatch leftThighTriangleBatch;
GLBatch leftUpperArmTriangleBatch;
GLBatch leftForeArmTriangleBatch;
GLBatch leftFinger1TriangleBatch;
GLBatch leftFinger2TriangleBatch;

GLBatch rightFootTriangleBatch;
GLBatch rightShankTriangleBatch;
GLBatch rightThighTriangleBatch;
GLBatch rightUpperArmTriangleBatch;
GLBatch rightForeArmTriangleBatch;
GLBatch rightFinger1TriangleBatch;
GLBatch rightFinger2TriangleBatch;

GLBatch neckTriangleBatch;

GLBatch bodySquareBatch;
GLBatch headSquareBatch;

GLfloat fStartX = 0.0f, fStartY = 0.0f;

// Left-Foot
GLfloat vLeftFootVerts[] = {
	fStartX - 0.02f - 0.1f, fStartY, 0.0f,
	fStartX - 0.02f, fStartY, 0.0f,
	fStartX - 0.07f, fStartY + 0.05f, 0.0f
};

// Left-Shank
GLfloat vLeftShankVerts[] = {
	fStartX - 0.07f, fStartY + 0.05f, 0.0f,
	fStartX - 0.02f - 0.02f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX - 0.02f - 0.08f, fStartY + 0.05f + 0.1f, 0.0f,
};

// Left-Thigh
GLfloat vLeftThightVerts[] = {
	fStartX - 0.02f - 0.08f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX - 0.02f - 0.02f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX - 0.07f, fStartY + 0.05f + 0.1f + 0.08f, 0.0f,
};

// Right-Foot
GLfloat vRightFootVerts[] = {
	fStartX + 0.02f + 0.1f, fStartY, 0.0f,
	fStartX + 0.02f, fStartY, 0.0f,
	fStartX + 0.07f, fStartY + 0.05f, 0.0f
};

// Right-Shank
GLfloat vRightShankVerts[] = {
	fStartX + 0.07f, fStartY + 0.05f, 0.0f,
	fStartX + 0.02f + 0.02f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX + 0.02f + 0.08f, fStartY + 0.05f + 0.1f, 0.0f,
};

// Right-Thigh
GLfloat vRightThightVerts[] = {
	fStartX + 0.02f + 0.08f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX + 0.02f + 0.02f, fStartY + 0.05f + 0.1f, 0.0f,
	fStartX + 0.07f, fStartY + 0.05f + 0.1f + 0.08f, 0.0f,
};

// Body
GLfloat vBodyVerts[] = {
	fStartX - 0.1f, fStartY + 0.05f + 0.1f + 0.08f, 0.0f,
	fStartX + 0.1f, fStartY + 0.05f + 0.1f + 0.08f, 0.0f,
	fStartX + 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f, 0.0f,
	fStartX - 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f, 0.0f,
};

// Neck
GLfloat vNeckVerts[] = {
	fStartX - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f, 0.0f,
	fStartX + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f, 0.0f,
	fStartX, fStartY + 0.05f + 0.1f + 0.08f + 0.15f + 0.05f, 0.0f,

};

// Head
GLfloat vHeadVerts[] = {
	fStartX, fStartY + 0.05f + 0.1f + 0.08f + 0.15f + 0.05f, 0.0f,
	fStartX + 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f + 0.05f + 0.05f, 0.0f,
	fStartX, fStartY + 0.05f + 0.1f + 0.08f + 0.15f + 0.05f + 0.05f + 0.1f, 0.0f,
	fStartX - 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.15f + 0.05f + 0.05f, 0.0f,

};

// Left-UpperArm
GLfloat vLeftUpperArmVerts[] = {
	fStartX - 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f - 0.03f, 0.0f,
	fStartX - 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX - 0.1f - 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f, 0.0f,

};

// Right-UpperArm
GLfloat vRightUpperArmVerts[] = {
	fStartX + 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f - 0.03f, 0.0f,
	fStartX + 0.1f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX + 0.1f + 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f, 0.0f,

};

// Left-ForeArm
GLfloat vLeftForeArmVerts[] = {
	fStartX - 0.1f - 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f - 0.03f, 0.0f,
};

// Right-ForeArm
GLfloat vRightForeArmVerts[] = {
	fStartX + 0.1f + 0.05f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f - 0.03f, 0.0f,
};

// Left-Finger-1
GLfloat vLeftFinger1Verts[] = {
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f - 0.02f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.015f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f, 0.0f,
};

// Left-Finger-2
GLfloat vLeftFinger2Verts[] = {
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f - 0.02f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.015f - 0.03f, 0.0f,
	fStartX - 0.1f - 0.05f - 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f - 0.03f, 0.0f,
};

// Right-Finger-1
GLfloat vRightFinger1Verts[] = {
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f + 0.02f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.015f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f, 0.0f,
};

// Right-Finger-2
GLfloat vRightFinger2Verts[] = {
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f + 0.02f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.015f - 0.03f, 0.0f,
	fStartX + 0.1f + 0.05f + 0.07f, fStartY + 0.05f + 0.1f + 0.08f + 0.075f + 0.03f - 0.03f - 0.03f, 0.0f,
};


void Rotate(GLfloat fOriginX, GLfloat fOriginY, GLfloat fRadius, GLfloat angle, GLfloat *pX, GLfloat *pY)
{
	static GLfloat fPi = 3.1415926f, fRad = fPi / 180.0f;
	*pX = fOriginX + fRadius * cos(angle * fRad);
	*pY = fOriginY + fRadius * sin(angle * fRad);
}

// 获取两点之间的距离
GLfloat GetDistanceBetweenTwoPoint(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
{
	// 根据勾股定理计算两点距离
	GLfloat a = x1 - x2;
	GLfloat b = y1 - y2;
	return sqrt(a * a + b * b);
}

// 挥舞手臂
void WaringArm()
{
	static GLint counter = 1;
	static GLfloat fMinAngle = 180.0f - 30.0f, fMaxAngle = 180.0f + 30.0f, fAngle = fMinAngle, fAngleInc = 1.0f;

	if (++counter > 30)
		counter = 0;
	else
		return;

	fAngle += fAngleInc;

	if (fAngle <= fMinAngle) {
		fAngle = fMinAngle;
		fAngleInc = 1;
	}
	else if (fAngle >= fMaxAngle) {
		fAngle = fMaxAngle;
		fAngleInc = -1;
	}

	// 肘关节的坐标点(x, y)
	GLfloat fLeftElbowX = vLeftForeArmVerts[0], fLeftElbowY = vLeftForeArmVerts[1];

	// 旋转点1
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftForeArmVerts[3], vLeftForeArmVerts[4]),
		fAngle - 20, &vLeftForeArmVerts[3], &vLeftForeArmVerts[4]
	);
	// 旋转点2
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftForeArmVerts[6], vLeftForeArmVerts[7]),
		fAngle + 20, &vLeftForeArmVerts[6], &vLeftForeArmVerts[7]
	);
	leftForeArmTriangleBatch.CopyVertexData3f(vLeftForeArmVerts);

	vRightForeArmVerts[3] = -vLeftForeArmVerts[3];
	vRightForeArmVerts[4] = vLeftForeArmVerts[4];

	vRightForeArmVerts[6] = -vLeftForeArmVerts[6];
	vRightForeArmVerts[7] = vLeftForeArmVerts[7];
	rightForeArmTriangleBatch.CopyVertexData3f(vRightForeArmVerts);

	// 旋转 Finger1 点1
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger1Verts[0], vLeftFinger1Verts[1]),
		fAngle - 6 - 11, &vLeftFinger1Verts[0], &vLeftFinger1Verts[1]
	);
	// 旋转 Finger1 点2
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger1Verts[3], vLeftFinger1Verts[4]),
		fAngle - 6, &vLeftFinger1Verts[3], &vLeftFinger1Verts[4]
	);
	// 旋转 Finger1 点3
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger1Verts[6], vLeftFinger1Verts[7]),
		fAngle - 6 + 11, &vLeftFinger1Verts[6], &vLeftFinger1Verts[7]
	);
	leftFinger1TriangleBatch.CopyVertexData3f(vLeftFinger1Verts);

	vRightFinger1Verts[0] = -vLeftFinger1Verts[0];
	vRightFinger1Verts[1] = vLeftFinger1Verts[1];

	vRightFinger1Verts[3] = -vLeftFinger1Verts[3];
	vRightFinger1Verts[4] = vLeftFinger1Verts[4];

	vRightFinger1Verts[6] = -vLeftFinger1Verts[6];
	vRightFinger1Verts[7] = vLeftFinger1Verts[7];
	rightFinger1TriangleBatch.CopyVertexData3f(vRightFinger1Verts);

	// 旋转 Finger2 点1
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger2Verts[0], vLeftFinger2Verts[1]),
		fAngle + 6 - 11, &vLeftFinger2Verts[0], &vLeftFinger2Verts[1]
	);
	// 旋转 Finger2 点2
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger2Verts[3], vLeftFinger2Verts[4]),
		fAngle + 6, &vLeftFinger2Verts[3], &vLeftFinger2Verts[4]
	);
	// 旋转 Finger2 点3
	Rotate(
		fLeftElbowX, fLeftElbowY,
		GetDistanceBetweenTwoPoint(fLeftElbowX, fLeftElbowY, vLeftFinger2Verts[6], vLeftFinger2Verts[7]),
		fAngle + 6 + 11, &vLeftFinger2Verts[6], &vLeftFinger2Verts[7]
	);
	leftFinger2TriangleBatch.CopyVertexData3f(vLeftFinger2Verts);

	vRightFinger2Verts[0] = -vLeftFinger2Verts[0];
	vRightFinger2Verts[1] = vLeftFinger2Verts[1];

	vRightFinger2Verts[3] = -vLeftFinger2Verts[3];
	vRightFinger2Verts[4] = vLeftFinger2Verts[4];

	vRightFinger2Verts[6] = -vLeftFinger2Verts[6];
	vRightFinger2Verts[7] = vLeftFinger2Verts[7];
	rightFinger2TriangleBatch.CopyVertexData3f(vRightFinger2Verts);
}

void AutoTriangleCopyVertexData3f(GLBatch *pTriangleBatch, GLfloat *vVerts)
{
	pTriangleBatch->Begin(GL_TRIANGLES, 3);
	pTriangleBatch->CopyVertexData3f(vVerts);
	pTriangleBatch->End();
}

void SetupRC()
{
        ......
	AutoTriangleCopyVertexData3f(&leftFootTriangleBatch, vLeftFootVerts);
	AutoTriangleCopyVertexData3f(&leftShankTriangleBatch, vLeftShankVerts);
	AutoTriangleCopyVertexData3f(&leftThighTriangleBatch, vLeftThightVerts);

	AutoTriangleCopyVertexData3f(&rightFootTriangleBatch, vRightFootVerts);
	AutoTriangleCopyVertexData3f(&rightShankTriangleBatch, vRightShankVerts);
	AutoTriangleCopyVertexData3f(&rightThighTriangleBatch, vRightThightVerts);

	bodySquareBatch.Begin(GL_TRIANGLE_FAN, 4);
	bodySquareBatch.CopyVertexData3f(vBodyVerts);
	bodySquareBatch.End();

	AutoTriangleCopyVertexData3f(&neckTriangleBatch, vNeckVerts);

	headSquareBatch.Begin(GL_TRIANGLE_FAN, 4);
	headSquareBatch.CopyVertexData3f(vHeadVerts);
	headSquareBatch.End();

	AutoTriangleCopyVertexData3f(&leftUpperArmTriangleBatch, vLeftUpperArmVerts);
	AutoTriangleCopyVertexData3f(&leftForeArmTriangleBatch, vLeftForeArmVerts);
	AutoTriangleCopyVertexData3f(&leftFinger1TriangleBatch, vLeftFinger1Verts);
	AutoTriangleCopyVertexData3f(&leftFinger2TriangleBatch, vLeftFinger2Verts);

	AutoTriangleCopyVertexData3f(&rightUpperArmTriangleBatch, vRightUpperArmVerts);
	AutoTriangleCopyVertexData3f(&rightForeArmTriangleBatch, vRightForeArmVerts);

	AutoTriangleCopyVertexData3f(&rightFinger1TriangleBatch, vRightFinger1Verts);
	AutoTriangleCopyVertexData3f(&rightFinger2TriangleBatch, vRightFinger2Verts);
}

void RenderScene(void)
{
        ......
	GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
	leftFootTriangleBatch.Draw();
	rightFootTriangleBatch.Draw();

	GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
	leftShankTriangleBatch.Draw();
	rightShankTriangleBatch.Draw();


	GLfloat vYellow[] = { 1.0f, 1.0f, 0.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vYellow);
	leftThighTriangleBatch.Draw();
	rightThighTriangleBatch.Draw();


	GLfloat vMagenta[] = { 1.0f, 0.0f, 1.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vMagenta);
	bodySquareBatch.Draw();


	GLfloat vCyan[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vCyan);
	neckTriangleBatch.Draw();


	GLfloat vPumpkinOrange[] = { 0.98f, 0.625f, 0.12f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vPumpkinOrange);
	headSquareBatch.Draw();


	GLfloat vPastelPink[] = { 0.98f, 0.04f, 0.70f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vPastelPink);
	leftUpperArmTriangleBatch.Draw();
	rightUpperArmTriangleBatch.Draw();

	GLfloat vPlum1[] = {  0.67f, 0.50f, 1.0f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vPlum1);
	leftForeArmTriangleBatch.Draw();
	rightForeArmTriangleBatch.Draw();

	GLfloat vOrchid1[] = { 1.0f, 0.51f, 0.98f, 1.0f };
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vOrchid1);
	leftFinger1TriangleBatch.Draw();
	leftFinger2TriangleBatch.Draw();
	rightFinger1TriangleBatch.Draw();
	rightFinger2TriangleBatch.Draw();

	WaringArm();

	glutSwapBuffers();
	glutPostRedisplay();
}

int main(int argc, char* argv[])
{
        .......
	return 0;
}

首先贴出运行的截图:

通过三角形和正方形进行绘制一个"简单的机器人"并且挥舞着双手向我们 "打招呼"

下面进行讲解上面的程序:

1. 三角形图形的绘制部分,是通过绘制多个三角形和四边形进行实现的,后面会贴出图片。(详细见机器人绘制图片)

2. 挥舞手臂的动作实现,通过旋转三角形,例如像 "Move" 的方向箭头通过旋转实现。(详细见挥舞手臂图片)

PS: 因为三角形的角度部分为大概计算的,所以旋转后的手臂并不完美,不过也算达到目的了。

机器人绘制图片:

挥舞手臂:

模仿我们摆动手臂的动作,进行做旋转操作。

好的,本章扩展学习内容部分就到此结束了,如有不懂的可评论留言,后续在进行补充。

最后祝大家七夕快乐~

本章源码:已上传到Github-learn-extension-Chapter02了哦,有兴趣的读者可以看下~

作者对游戏的说明: 首先,您应当以一种批判的眼光来看待本程序。这个游戏是我制作 的第一部RPG游戏,无任何经验可谈,完全按照自己对游戏的理解进 行设计的。当我参照了《圣剑英雄2》的源码之后,才体会到专业游 戏引擎的博大精深。 该程序的内核大约有2000余行,能够处理人物的行走、对话、战斗, 等等。由于该程序的结构并不适于这种规模的程序,故不推荐您详 细研究该程序。所附地图编辑器的源程序我已经添加了详细的注释, 其程序结构也比较合理,可以作为初学VC的例子。 该程序在VC的程序向导所生成的SDI框架的基础上修改而成。它没有 使用任何关于VC底层的东西。程序的绝大部分都是在CgameView类中 制作的,只有修改窗口特征的一段代码在CMainFrm类中。其他的类 统统没有用到。另外添加的一个类是CEnemy类。 整个游戏的故事情节分成8段,分别由Para1.h ~ Para8.h八个文件 实现。由于程序仅仅能够被动的处理各种各样的消息,所以情节的 实现也只能根据系统的一些参数来判断当前应当做什么。在程序中 使用了冗长的if……else if……结构来实现这种判断。 当然,在我的记录本上,详细的记录了每个事件的判断条件。这种 笨拙的设计当然是不可取的。成都金点所作《圣剑英雄II》采用了 剧本解读的方式,这才是正统的做法。但这也需要更多的编程经验 和熟练的code功夫。 下面列举的是程序编制过程中总结出来的经验和教训。 第一,对话方式应该采用《圣剑英雄II》的剧本方式。 现在的方式把一个段落中所有的对话都混在一个文件中,然后给每 句话一个号码相对应。这样做虽然降低了引擎的难度,却导致剧情的 编写极其繁琐。 第二,运动和显示应当完全分开。 现在的程序中,运动和显示是完全同步的。即:在定时器中调用所有 敌人的运动函数,然后将主角的动画向前推一帧,接着绘制地图,调 用所有敌人的显示函数、重绘主角。这样的好处是不会掉帧,但带来 的问题是,如果要提高敌人的运动速度,那么帧数也跟着上去了。所 以当DEMO版反馈说速度太慢的时候,我修改起来非常困难。而这个问 题到最后也仅仅是将4步一格该成了2步一格。 第三,VC中数组存在上限。如果用“int aaa[1000000000]”定义一个 数组,编译器肯定不会给分配那么大的内存空间。而在这个程序中, 地图矩阵、NPC矩阵都超过了VC中数组的上限。但这一点知道的太晚了。 在1.0版本中已经发现地图最右端缺少了几行,但不知道是什么原因 造成的。(地图编辑器中未出现此问题,因为地图编辑器是用“序列 化”的方式存盘读盘的。)解决这个问题的方法是用“new”来分配 内存空间。 第四,由于不知道应该如何使用“new”和“delete”,几乎所有的DC 都使用了全局变量。这是完全没有必要的。程序运行期大约会耗用20 多M的内存空间,相当于一个大型游戏所使用的内存空间了。 另外,在游戏的剧情、美工方面也有许多问题,总之一个词“业余”。 我就不总结了。下一部作品,我将争取在程序上有一个质的飞跃。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值