深圳大学计算机图形学实验一——OpenGL绘制布布头像

        尝试利用OpenGL绘制一些可爱的表情包。

        一二布布是较为流行的一套萌系表情包。我选择绘制一二布布系列的一张表情包。这张表情包描绘了趴在窗户上的一只小棕熊布布。

        表情包印在手机壳的样例如下:

深圳大学计算机图形学实验一——OpenGL绘制布布头像_第1张图片

        头的绘制。布布的头并不是一个简单的椭圆形。它上窄下宽,并不关于x轴对称。并且它在\pi /43\pi/4方向比椭圆更加突出,本质上更加贴近圆角矩形。因此,无法用椭圆拼凑出布布的头。

        利用desmos函数工具,寻找对应的函数。利用公式绘制出布布的头。

深圳大学计算机图形学实验一——OpenGL绘制布布头像_第2张图片

        此数学函数并不在预设中,因此另外编写函数绘制蚶线函数图。

深圳大学计算机图形学实验一——OpenGL绘制布布头像_第3张图片

        椭圆形手臂也用类似的方法计算。求任意方向椭圆顶点位置的代码为:

for (int i = 0; i < ELLIPSE_NUM_POINTS; i++) {
		double sita = i * 2 * M_PI / ELLIPSE_NUM_POINTS;//寮у害
		double r = 1.0/(1-a*cos(sita-b));//妯¢暱
		double x;
		if(type==0)
			x = -0.3*  scale * r * cos(sita);
		else if(type==1)
			x = 0.3*scale * r * cos(sita);
		double y = 0.3*scale* r * sin(sita);
		conic_vertices[i] = glm::vec2(x, y)+center;
		conic_colors[i] = color;
	}

        使用圆锥曲线的极坐标方程计算顶点位置。

深圳大学计算机图形学实验一——OpenGL绘制布布头像_第4张图片

        本图像由35个椭圆、蚶线图像组合而成。生成这么多图形,我们不可能每个图形都复制一遍生成点、链接、绘制的代码。因此我们把生成椭圆和生成蚶线的代码分别提取出来,抽离成一个函数。这样做使得代码更加简洁且易于维护。在生成椭圆时也更加简单,无需一处处修改代码,只需要在调用函数时修改位置、大小、颜色、离心率等数值即可。 

        布布趴在玻璃上,额头、下巴、手和脸颊都压扁在玻璃上,产生白色的印记。额头和下巴印记的图像也不是标准的椭圆。因此我们修改函数,给生成椭圆的函数增加一个参数type。如果type=0,则生成正常椭圆,type=1,则生成变体椭圆。变体椭圆对y坐标进行进一步处理。将所有顶点y坐标乘以一个值,离y轴越近,除的越多,离y轴越远除的越少,以此达到把椭圆进一步拍扁,形成近似圆角矩形的效果。

        图像的叠放次序是十分重要的。本图像中35个图形的叠放形成了最终结果。

        边框的绘制方法是:先绘制一个深色图形,再绘制一个稍小的浅色图形遮盖深色图形的大部分面积。深色图形未被遮盖的部分即为深色边框。

        耳朵的绘制方法是:绘制一个深色椭圆,原位绘制一个浅色椭圆,然后在靠近头的部分绘制一个深色椭圆。每个耳朵由三个椭圆拼接而成,三个椭圆绘制完毕后,绘制头部的蚶线图像,把这些椭圆的一部分遮掉,露出的部分就是耳朵。

        嘴巴的绘制方法是:绘制两个深色椭圆,然后在上方绘制数个浅色椭圆遮掉部分深色。露出的部分形成微笑的嘴部表情。

        眼角挤扁细节的绘制方法是:绘制一个深色椭圆,然后在外侧绘制一个浅色椭圆把深色椭圆的大部分遮盖掉,露出部分剩余一段弧形。注意,需要先绘制眼角纹理再绘制脸颊挤在玻璃上的腮红。否则腮红会被棕色椭圆遮盖掉。

        手和身体的绘制方法:先绘制脸和身体的轮廓,然后画上腮部色块,然后画上脸。然后画上双手轮廓和双手棕色部分,这样子形成手的椭圆就会遮盖掉形成脸的蚶线图像,形成手抱住脸的形态。绘制完双手后再绘制身体的棕色部分,这个棕色椭圆形可以遮掉脸部的下侧棕色边框和双手的内侧棕色边框。合理的叠放次序造就了可爱的布布。

        最终的绘制结果:

深圳大学计算机图形学实验一——OpenGL绘制布布头像_第5张图片

        全部代码:

#include "Angel.h"
#include 
const glm::vec3 WHITE(1.0, 1.0, 1.0);
const glm::vec3 BLACK(0.0, 0.0, 0.0);
const glm::vec3 RED(1.0, 0.0, 0.0);
const glm::vec3 GREEN(0.0, 1.0, 0.0);
const glm::vec3 BLUE(0.0, 0.0, 1.0); 
const int CIRCLE_NUM_POINTS = 100;
const int ELLIPSE_NUM_POINTS = 100;
const int TRIANGLE_NUM_POINTS  = 3;
const int SQUARE_NUM  = 6;
const int SQUARE_NUM_POINTS  = 4 * SQUARE_NUM;
const int FUNCTION_1_NUM_POINTS = 2000;

// 每当窗口改变大小,GLFW会调用这个函数并填充相应的参数\\
] ;ODT
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

// 根据角度生成颜色
float generateAngleColor(double angle)
{
	return 1.0 / (2 * M_PI) * angle;
}

// 计算椭圆/圆上的点
glm::vec2 getEllipseVertex(glm::vec2 center, double scale, double verticalScale, double angle,int type)//type==1生成变体
{
	glm::vec2 vertex(sin(angle), cos(angle));
	vertex *= scale;
	vertex.y /= verticalScale;
	if(type==1)
		vertex.y /= (1.25 - abs(vertex.x));
	vertex += center;  
	return vertex;
}
//计算部分椭圆的点

// 获得椭圆/圆的每个顶点
void generateEllipsePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex, int numPoints,
	glm::vec2 center, double scale, double verticalScale,glm::vec3 color,int type)//开始和结束的弧度
{
	double angleIncrement = (2 * M_PI) / numPoints;
	double currentAngle = M_PI/2;

	for (int i = startVertexIndex; i < startVertexIndex + numPoints; ++i) {
		vertices[i] = getEllipseVertex(center, scale, verticalScale, currentAngle,type);
		if (verticalScale == 1.0) {//圆
			colors[i] = glm::vec3(generateAngleColor(currentAngle), 0.0, 0.0);
		} else {
			colors[i] = color;
		}
		currentAngle += angleIncrement;
	}
}
//获得部分椭圆的点(没什么用)
void generatePartEllipsePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex, int numPoints,
	glm::vec2 center, double scale, double verticalScale, glm::vec3 color,double startr,double endr)
{
	double angleIncrement = (2 * M_PI) / numPoints;
	double currentAngle = M_PI / 2;

	for (int i = startVertexIndex; i < startVertexIndex + numPoints; ++i) {
		//if (currentAngle>startr&¤tAngle

你可能感兴趣的:(计算机图形学,c++)