只是一个简单的个人主页 。

我是一个好人,很好很好的人,因为我选了图形学这门课。

关于CG

我觉得CG是让我们接受一个新的内容的开始,一个好的CG可以让我们对它的主体充满兴趣,一个差的CG会让我直接忽视其内容的存在。我也希望自己可以通过图形学的学习,让自己变成一个富有图形学色彩的人。

我的任务

以下是我的图形学课程的课程任务:

任务一:行走的小人(OpenGL实现)

实验完成中遇到的问题和解决办法:

1、选定腰部节点为根节点。这样可以使小人的上半身和下半身按照相同的运动函数进行移动。


//画臀部,根节点
void drawHip() {
	glScalef(1.5, 0.5, 1.0);
	//glPushMatrix();
	glTranslatef(0.0, -1.0, 0.0);
	glutWireCube(0.3);
	drawTorso();

	drawLeftleg1();
	drawRightleg1();
	glPopMatrix();//回到起始点
}

2、绘制立方体和圆柱体。小人的躯干采用长方体实现,而四肢采用圆柱体,不仅可以良好的区分两部分,更符合人体的圆柱形手臂摆动规律。


//画躯干
void drawTorso() {
	glScalef(0.5, 2.0, 1.0);  //先还原成1:1
	glScalef(0.5, 1.5, 0.5);  //躯干Z轴方向缩短一半,不然躯干太粗
	glPushMatrix();

	glTranslatef(0.0, 0.35, 0.0);
	glutWireCube(0.6);
	drawShoulder();

	glPopMatrix();
}
void drawRightarm2() {...}
void drawRightarm1() {...}
void drawLeftarm2() {...}
void drawLeftarm1() {...}

3、移动函数的构造。因为小人是有大臂和小臂的,在大臂摆动的同时,小臂要实时更新肘关节位置以保证手臂不散架,并且两个臂的摆动频率应相同,而腿部的运动也具有相同的特点。


//以左臂为例
void selfRotateWholeLA() {
	static float rotateAngle = 0;
	static int times1 = 0;
	static int x = 1;
	//static int y = 1;
	//static int z = 1;
	times1++;
	if (times1 > 20)
	{
		times1 = 0;

	}

	if (times1 % 20 == 0)
	{
		rotateAngle += 0.1;
		if (rotateAngle > 20)
		{
			rotateAngle = -rotateAngle;
			x = -x;
			//y = -y;
			//z = -z;
		}
	}

	glRotatef(rotateAngle, x, 0, 0);

}

4、整体的平移。为了让小人走起来,在函数实现时,应先移动坐标轴(矩阵点)然后再绘制身体各部分,四肢的移动也应在整体前进的函数之后实现。


move();
drawHip();//绘制身体

5、调试。对视角的选定、窗口大小、小人运动方向以及速度等因素,应不断调试以达到最佳效果。结果如下

任务二:1° 光线追踪(OpenGL实现)

实验操作步骤:

1、学会使用OpenGL库中的光线追踪函数。

2、设置环境光,漫反射光,镜面光以及反射光指数。


GLfloat mat_ambient[] = { 0.2f,0.2f,0.2f,1.0f };//环境光
GLfloat mat_diffuse[] = { 0.8f,0.8f,0.8f,1.0f };//漫反射
GLfloat mat_specular[] = { 1.0f,1.0f,1.0f,1.0f };//镜面光
GLfloat mat_shininess[] = { 50.0f };//镜面反射指数

glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);//设置环境光源
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//设置漫反射光源
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);//设置镜面反射光源
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);//设置镜面反射指数

3、设置灯源位置和光源散布角度。


glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);//设置0号灯的漫反射光源
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);//设置0号灯的光源位置(上方光源)

glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);//设置1号灯的环境光源
glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);//设置1号灯的镜面反射光
glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);//设置1号灯的漫反射光
glLightfv(GL_LIGHT1, GL_POSITION, light0_position);//重置0号灯位置(下方)


glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 45.0);//定义光源最大散布角度为30,默认是180
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);//指定光照方向
glEnable(GL_LIGHTING);//打开灯光

4、调试。对视角的选定、窗口大小、大小和颜色等因素,应不断调试以达到最佳效果。


glViewport(0, 0, w, h);//把视口设置为窗口大小
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if (w <= h)
	glOrtho(-5.5f, 5.5f, -5.5f*h / w, 5.5f*h / w, -10.0f, 10.0f);//正射投影,物体大小不变
else
	glOrtho(-5.5f*w / h, 5.5f*w / h, -5.5f, 5.5f, -10.0f, 10.0f);

glutInitWindowPosition(100, 100);//设置窗口位置
glutInitWindowSize(300, 300);//设置窗口大小

5、结果展示

2° Gouraud shading的代码实现(MFC实现)

实验完成中遇到的问题和解决办法:

1、初始化。设置不同的位置和RPG值的斜率参数。


double slope_start;//扫描线开始的斜率
double slope_end;//扫描线结束的斜率

int r1, g1, b1, r2, g2, b2, r3, g3, b3;

double slope_start_r, slope_start_g, slope_start_b;
double slope_end_r, slope_end_g, slope_end_b;//计算三个顶点相关的颜色斜率

int x, y;
int xs, xe;//扫描线开始和结束处的像素

double dx_start = 0, dx_end = 0;//循环内的斜率变化值,采用加法比乘法快

double dr_start = 0, dg_start = 0, db_start = 0;
double dr_end = 0, dg_end = 0, db_end = 0;

double rs, re, gs, ge, bs, be;//循环内对每条扫描线的起始和终止颜色倒斜率
double dr_x, dg_x, db_x;
double r, g, b;
double dx; int dy;

2、判断三角形顶点位置。获取最左上角三角形顶点位置,并按照从宽到窄的方向绘制扫描线。


//平顶情况
if (pt[v1].y == pt[v2].y || pt[v1].y == pt[v3].y)
{
	//交换,使pt[v1]点为最左边的顶点
	if (pt[v1].y == pt[v2].y)
	{
		if (pt[v1].x > pt[v2].x)
			SWAP(v1, v2);
	}
	else
	{
		if (pt[v1].x > pt[v3].x)
			SWAP(v1, v3);
	}

	//交换,使平顶三角形的左上为pt[v1],右上为pt[v3],下顶点为pt[v2]
	if (pt[v3].y > pt[v2].y)
		SWAP(v2, v3);

	dy = pt[v2].y - pt[v1].y;

	slope_start = (pt[v2].x - pt[v1].x)*1.0 / dy;//(pt[v2].y-pt[v1].y);
	slope_end = (pt[v2].x - pt[v3].x)*1.0 / dy;//(pt[v3].y-pt[v2].y);

	GetRGBValue(color[v1], r1, g1, b1);
	GetRGBValue(color[v2], r2, g2, b2);
	GetRGBValue(color[v3], r3, g3, b3);

	slope_start_r = (r2 - r1)*1.0 / dy;
	slope_start_g = (g2 - g1)*1.0 / dy;
	slope_start_b = (b2 - b1)*1.0 / dy;

	slope_end_r = (r2 - r3)*1.0 / dy;
	slope_end_g = (g2 - g3)*1.0 / dy;
	slope_end_b = (b2 - b3)*1.0 / dy;
}

3、绘制扫描线。对三个定点的位置和RPG值进行纵向插值,每次的纵向插值过程中再进行横向插值,使颜色过渡自然。


//循环执行扫描线
for (y = pt[v1].y; y <= pt[v2].y; y++)
{
	xs = Myfloor(pt[v1].x + dx_start);
	xe = Myceil(pt[v3].x + dx_end);

	rs = r1 + dr_start;
	re = r3 + dr_end;
	gs = g1 + dg_start;
	ge = g3 + dg_end;
	bs = b1 + db_start;
	be = b3 + db_end;

	dx = max(xe - xs, 0.0001);//扫描线长度
	dr_x = (re - rs)*1.0 / dx;
	dg_x = (ge - gs)*1.0 / dx;
	db_x = (be - bs)*1.0 / dx;

	r = rs;
	g = gs;
	b = bs;
	//对扫描线的颜色进行横向插值,使之从左向右的颜色过渡是自然的
	for (x = xs; x <= xe; x++)
	{
		COLORREF clr = RGB(min(r, 255), min(g, 255), min(b, 255));

		pDC->SetPixel(x, y, clr);//绘制扫描线
		//扫描线的颜色变化
		r += dr_x;
		g += dg_x;
		b += db_x;
	}

	dx_start += slope_start;
	dx_end += slope_end;

	dr_start += slope_start_r;
	dg_start += slope_start_g;
	db_start += slope_start_b;
	dr_end += slope_end_r;
	dg_end += slope_end_g;
	db_end += slope_end_b;
		}										

4、调用上述函数。改写View.cpp中的OnDraw(),绘制三角形轮廓,对顶点填充不同颜色,调用函数进行Gouraud shading渲染。


void CMFCApplication12View::OnDraw(CDC* pDC)
{
	CMFCApplication12Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	
	CPen MyPen, *OldPen;
	MyPen.CreatePen(PS_DASH, 0, RGB(0, 0, 255));
	OldPen = pDC->SelectObject(&MyPen);

	CBrush MyBrush, *OldBrush;
	MyBrush.CreateSolidBrush(RGB(0, 0, 255));
	OldBrush = pDC->SelectObject(&MyBrush);
	//原始图像
	CPoint pt0[3] = { CPoint(100,100),CPoint(400,100), CPoint(350,400) };
	pDC->Polygon(pt0, 3);

	CPoint pt2[3] = { CPoint(500,100),CPoint(800,100), CPoint(750,400) };
	pDC->Polygon(pt2, 3);

	COLORREF color[3] = { pDC->GetPixel(pt2[0].x, pt2[0].y), 16646660, 16646000 };

	GouraudShading(pt2, color, pDC);

	//清除  
	pDC->SelectObject(OldPen);
	MyPen.DeleteObject();
	pDC->SelectObject(OldBrush);
	MyBrush.DeleteObject();
}

												

5、调试。对窗口大小、三角形大小以及各顶点颜色等因素进行调试,以达到最佳效果。

任务三:Final Project

1、实验要求不使用自带的光线追踪函数,因此我自己写了一些基本的ray-tracing方法,并利用这些方法对简单物体进行光线追踪


												
//L 入射光,R 反射光,N 法向量,V 视点方向,H L和V的角平分线
Point L,R,N,V,H,I;
L = (info.intersection_point - ray).normal();
N = (info.norm).normal();
R = (Point::reflection(L,N)).normal();
V = (info.start - info.intersection_point).normal();
I = intensity;

if (!environment_light_only)
	//color = color + Color(suc_mul(I,mod->material->ka)); 
//else
color = color + Color(suc_mul(Settings::surrounding_light,mod->material->ka)) 
	+ Color(suc_mul(I,mod->material->ks)*pow(dot(R,V),mod->material->n)) 
	+ Color(suc_mul(I,mod->material->kd)*dot(-1*L,N));
else
	color = color +Color(suc_mul(Settings::surrounding_light,mod->material->ka));

2、根据上一次做简单ray-tracing的经验,我将上一个任务的光线计算方法运用在这一次实验中,具体方法可见上一个实验。

3、构造场景。在场景中我们画出地板,左侧墙壁,并在地板上绘制出一些不同大小和颜色的小球与长方体,然后利用ray-tracing和shading方法对他们进行再绘制。


/***   生成红色长方体   ***/
Model* arectangle = new Model();	//新建矩形模型
Material * arectangle_mat = new Material(0,0,Point(0,0,0),Point(0,0,0));	//矩形材质
General_model * arectangle_geo = new General_model();
arectangle_mat->inital(Point(0.1,0.1,0.1),Point(0,0.3,0.4),Point(0.2,0.1,0.5),3);	//初始化
//添加矩形的8个顶点
arectangle_geo->add_vertex(Point(0,13000,0));
arectangle_geo->add_vertex(Point(3000,13000,0));
arectangle_geo->add_vertex(Point(3000,16000,0));
arectangle_geo->add_vertex(Point(0,16000,0));
arectangle_geo->add_vertex(Point(0,13000,3000));
arectangle_geo->add_vertex(Point(3000,13000,3000));
arectangle_geo->add_vertex(Point(3000,16000,3000));
arectangle_geo->add_vertex(Point(0,16000,3000));
//根据顶点添加表面
arectangle_geo->add_face(0,1,2);
arectangle_geo->add_face(0,2,3);
arectangle_geo->add_face(0,1,5);
arectangle_geo->add_face(0,5,4);
arectangle_geo->add_face(1,2,6);
arectangle_geo->add_face(1,6,5);
arectangle_geo->add_face(4,5,6);
arectangle_geo->add_face(4,6,7);
//构建几何形状
arectangle->add_geometry(arectangle_geo);
//构造表面材质
arectangle->add_material(arectangle_mat);
//初始化
arectangle->geometry->inital();
//在屏幕中加载
scene->add_model(arectangle);

//进行ray-tracing
raySource->inital(Point(12000,0,8000));
//初始化相机位置
camera->initalize(Point(15000,0,2500),Point(-1,2,0),Point(0,0,1),Settings::scLen,Settings::scWid,100);

//对camera进行设置
camera->pixmap = pixmap;
camera->scene = scene;
camera->screen = screen;	

//对场景进行ray-tracing
scene->add_raySource(raySource);
scene->add_illumin(illumin);
camera->castLight();
camera->pixmap->show();

4、实验结果展示:

fig.1. 场景中的所有元素都没有进行ray-tracing:

fig.2. 对场景中地板以外的元素进行ray-tracing:

fig.3. 对场景中所有元素进行ray-tracing:

我的联系方式

QQ:596356873

TEL:17865169889

Email:596356873@qq.com