OpenGL绘制光照和材质效果

OpenGL绘制光照和材质效果


在这里插入图片描述

本次任务主要实践三维空间的平移旋转、透视投影、光照及材质。有关阴影效果,课程的本节内容仅讲述了手动操作模型矩阵来绘制的阴影平面,而主流的阴影绘制方法是利用纹理和贴图来渲染阴影。


1. 将三个物体同轴排列

前面几个小节已经绘制了平面五角星、三维彩色立方体以及递归细分四面体法的三维球体,本节的要求首先是将三个物体同轴排列,使之在视景体中可见并设置透视投影来观察三者。为了更好地观察空间中的物体,本代码中额外绘制了三维坐标架,并绘制出了XOY平面,相关代码如下:

void frame()
{
	// 绘制三维坐标架
	glColor3f(0.3f, 0.3f, 0.3f);
	glLineWidth(0.75);
	glBegin(GL_LINES);
	glVertex3f(-100.0f, 0.0f, 0.0f);
	glVertex3f(100.0f, 00.0f, 0.0f);
	glVertex3f(0.0f, -100.0f, 0.0f);
	glVertex3f(0.0f, 100.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, -100.0f);
	glVertex3f(0.0f, 0.0f, 100.0f);
	glEnd();
	// 绘制XOY平面
	glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_QUADS);
	glVertex3f(8.0f, 8.0f, 0.0f);
	glVertex3f(-8.0f, 8.0f, 0.0f);
	glVertex3f(-8.0f, -8.0f, 0.0f);
	glVertex3f(8.0f, -8.0f, 0.0f);
	glEnd();
}

在这里插入图片描述

绘制好坐标架后,三个物品依次排列时就可以清晰地看出其空间关系。三个物体绘制的具体代码不再赘述,以下是display函数中将三个物体依次排列的相关代码:

void display()
{
	// 设置背景为白色
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// 加载单位阵
	glLoadIdentity();
	// 设置相机的位置和视角
	gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
	// 绘制坐标架以及XOY平面
	frame();
	// 平移坐标系,让三个待绘制物品的底部排列在X轴上
	glTranslatef(0.0f, 0.0f, 1.0f);
	// 在中心绘制三维彩色立方体,并绘制简单阴影
	glTranslatef(-0.75f, -0.75f, -0.75f);
	color_cube(1.5f);
	cube_shadow(0.0f, 0.0f, 6.0f, 1.5f);
	glTranslatef(0.75f, 0.75f, 0.75f);
	// 在左侧(Y轴负半轴)绘制红色球面
	glTranslatef(0.0f, -4.0f, 0.0f);
	sphere();
	glTranslatef(0.0f, 4.0f, 0.0f);
	// 在右侧(Y轴正半轴)绘制平面的五角星
	glTranslatef(0.0f, 4.0f, 0.0f);
	pentagram();
	glTranslatef(0.0f, -4.0f, 0.0f);
	// 刷新帧缓存
	glutSwapBuffers();
}

在不添加光照和阴影效果时,三个物品依次排列绘制出的效果如下:

在这里插入图片描述

可以看出,我们将三个物体的中心排列在同一高度,但由于使用了透视投影,靠右侧的五角星在视觉上要比靠左侧的球体更大,但实际上五角星的外接圆半径与球体半径是相同的,均为1。但由于没有使用光照效果,球体看上去只是一个圆(透视投影下被拉成椭圆)。


2. 光照、材质与简单阴影效果

光照和材质效果可以在init()函数中设置,此外在使用光照时,需要在绘制图像过程中增加每一点法向量的设置。指定法线向量的方式与指定颜色的方式有雷同之处。在指定颜色时,只需要指定每一个顶点的颜色,OpenGL就可以自行计算顶点之间的其它点的颜色。并且,颜色一旦被指定,除非再指定新的颜色,否则以后指定的所有顶点都将以这一向量作为自己的颜色。在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点之间的其它点的法线向量。并且,法线向量一旦被指定,除非再指定新的法线向量,否则以后指定的所有顶点都将以这一向量作为自己的法线向量。使用glColor*()函数可以指定颜色,而使用glNormal*()函数则可以指定法线向量。

在OpenGL中,仅仅支持有限数量的光源。使用GL_LIGHT0表示第0号光源,GL_LIGHT1表示第1号光源,依次类推。OpenGL至少会支持8个光源,即GL_LIGHT0到GL_LIGHT7。每一个光源都可以设置其属性,这一动作是通过glLight*()函数完成的。glLight*()函数具有三个参数,第一个参数指明是设置哪一个光源的属性,第二个参数指明是设置该光源的哪一个属性,第三个参数则是指明把该属性值设置成多少。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性表示了光源所发出的光的反射特性(以及颜色),每个属性由四个值表示,分别代表了颜色的R, G, B, A值。GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。GL_DIFFUSE表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)GL_SPECULAR表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。GL_POSITION属性表示光源所在的位置。由四个值(X, Y, Z, W)表示。如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。如果第四个值W不为零,则X/W, Y/W, Z/W表示了光源的位置。这种光源称为位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,各种矩阵变换函数例如:glTranslate*()、glRotate*()等在这里也同样有效。方向性光源在计算时比位置性光源快了不少,因此,在视觉效果允许的情况下,应该尽可能的使用方向性光源。

材质与光源相似,也需要设置众多的属性。不同的是,光源是通过glLight*()函数来设置的,而材质则是通过glMaterial*()函数来设置的。glMaterial*()函数有三个参数。第一个参数表示指定哪一面的属性。可以是GL_FRONT、GL_BACK或者GL_FRONT_AND_BACK。分别表示设置“正面”“背面”的材质,或者两面同时设置。第二、第三个参数与glLight*函数的第二、三个参数作用类似。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。GL_SHININESS属性只有一个值,称为“镜面指数”,取值范围是0到128。该值越小,表示材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。该值越大,表示材质越类似于镜面,光源照射到上面后,产生较小的亮点。GL_EMISSION属性由四个值组成,表示一种颜色。OpenGL认为该材质本身就微微的向外发射光线,以至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其它物体的颜色。

void init()
{
	// 计算五角星有关数据
	get_pentagram();
	// 设置逆时针排列的点围成的平面为正面
	glFrontFace(GL_CCW);
	// 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);
	// 启用抗锯齿(使线平滑)
	glEnable(GL_BLEND);
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	// 设置材质和光照的信息
	// 有关光照与材质:https://blog.csdn.net/timidsmile/article/details/7017197
	GLfloat mat_ambient[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
	GLfloat mat_diffuse[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
	GLfloat mat_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat mat_shininess[4] = { 100.0f };
	GLfloat light_ambient[4] = { 0.2f, 0.2f, 0.2f, 0.0f };
	GLfloat light_diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat light_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat light_position[4] = { 0.0f, 6.0f, 0.0f, 1.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);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	// 设置颜色材料,使光照模式下仍然可以显示原本的颜色
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);
	// 启用平滑着色功能
	glShadeModel(GL_SMOOTH);
	// 启用光照功能
	glEnable(GL_LIGHTING);
	// 启用0号光源
	glEnable(GL_LIGHT0);
	// 启用检测深度
	glEnable(GL_DEPTH_TEST);
	// 环境颜色设置为白色
	glClearColor(1.0, 1.0, 1.0, 1.0);
}

在这里插入图片描述

可以看出,在(0, 0, 6)处设置一点光源,从上方照射三个物体,物体的侧面由于受到光照较少,会产生出阴影的质感。下面是变换光源位置以及光源三种属性后的不同效果。为了更好地展现光照效果,此处将环境颜色设置成深色。

从Z轴负向,即底部向上照射三个物体:

在这里插入图片描述

从顶部向下照射三个物体,但去除了环境光,只保留漫反射和镜面反射:

在这里插入图片描述

光照之后的任务要求即为绘制阴影。阴影是一种高级光照渲染,会为物体进一步增加立体感。阴影一般由深度贴图渲染实现,本节的课件中介绍了一种简单的绘制阴影的方法,即手动操作变换矩阵,将原物品直接映射到一个平面上。这种方法需要绘制原物品两次,并且需要手动计算阴影变换矩阵,不太灵活,代码耦合性较高且手动计算复杂。下面仅实现了立方体的阴影:

在这里插入图片描述

void cube_shadow(GLfloat x, GLfloat y, GLfloat z, GLfloat size)
{
	GLfloat m[16] = { 0.0f };
	m[0] = m[5] = m[10] = 1.0f;
	m[11] = -1.0f / z;
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(x, y, z);
	glMultMatrixf(m);
	glTranslatef(-x, -y, -z);
	color_cube(size);
	glPopMatrix();
}

添加阴影后效果如下,但可以看出阴影也是绘制出的多面体,同样也要受到全局光照的影响,其原本的颜色在光照下变亮,显然不符合阴影的效果。如果要修改,需要改动多面体本身的绘制代码,单独添加与光照有关的设置,非常麻烦,本代码中没有涉及,仅仅实践了简单的阴影绘制方法而已:

在这里插入图片描述


附录:完整代码

#include<GL/glut.h>
#include<math.h>
#include<iostream>
#define DEPTH 4
#define PI 3.1415926
using namespace std;

void frame()
{
	// 绘制三维坐标架
	glColor3f(0.3f, 0.3f, 0.3f);
	glLineWidth(0.75);
	glBegin(GL_LINES);
	glVertex3f(-100.0f, 0.0f, 0.0f);
	glVertex3f(100.0f, 00.0f, 0.0f);
	glVertex3f(0.0f, -100.0f, 0.0f);
	glVertex3f(0.0f, 100.0f, 0.0f);
	glVertex3f(0.0f, 0.0f, -100.0f);
	glVertex3f(0.0f, 0.0f, 100.0f);
	glEnd();
	// 绘制XOY平面
	glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_QUADS);
	glVertex3f(8.0f, 8.0f, 0.0f);
	glVertex3f(-8.0f, 8.0f, 0.0f);
	glVertex3f(-8.0f, -8.0f, 0.0f);
	glVertex3f(8.0f, -8.0f, 0.0f);
	glEnd();
}

void normalize(GLfloat* v)
{
	GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	v[0] /= d; v[1] /= d; v[2] /= d;
}

void divide_triangle(GLfloat* a, GLfloat* b, GLfloat* c, int depth)
{
	if (depth > 0) {
		GLfloat ab[3], ac[3], bc[3];
		for (unsigned int i = 0; i < 3; i++)
			ab[i] = a[i] + b[i];
		normalize(ab);
		for (unsigned int i = 0; i < 3; i++)
			ac[i] = a[i] + c[i];
		normalize(ac);
		for (unsigned int i = 0; i < 3; i++)
			bc[i] = b[i] + c[i];
		normalize(bc);
		divide_triangle(a, ab, ac, depth - 1);
		divide_triangle(b, bc, ab, depth - 1);
		divide_triangle(c, ac, bc, depth - 1);
		divide_triangle(ab, bc, ac, depth - 1);
	}
	else {
		glBegin(GL_TRIANGLES);
		glNormal3fv(a);
		glVertex3fv(a);
		glNormal3fv(b);
		glVertex3fv(b);
		glNormal3fv(c);
		glVertex3fv(c);
		glEnd();
	}
}

void sphere()
{
	GLfloat tetrahedron_vertex[][3] = {
		0.0f,		 0.0f,		 1.0f,
		0.0f,		 0.942809f, -0.333333f,
	   -0.816497f, -0.471405f, -0.333333f,
		0.816497f, -0.471405f, -0.333333f
	};
	glColor3f(1.00f, 0.00f, 0.00f);
	divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[2], tetrahedron_vertex[1], DEPTH);
	divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[3], tetrahedron_vertex[2], DEPTH);
	divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[1], tetrahedron_vertex[3], DEPTH);
	divide_triangle(tetrahedron_vertex[1], tetrahedron_vertex[2], tetrahedron_vertex[3], DEPTH);
}

GLfloat pentagram_vertex[5][3];
GLfloat pentagon_vertex[5][3];

void get_pentagram()
{
	// 五角星看作在圆上内接的五边形的顶点连成的,此处设置外接圆半径[0, 1]
	GLfloat r = 1.0f;
	// 五角星可以在外接圆上旋转,此处设置旋转角度[0, 360)
	GLfloat rotate = 18.0;
	// 依次通过外接圆计算正五边形的五个顶点横纵坐标
	GLfloat tmp[5][2];
	for (int i = 0; i < 5; i++) {
		tmp[i][0] = (GLfloat)(r * cos(((72.0 * (GLfloat)i + rotate) / 360.0) * (2 * PI)));
		tmp[i][1] = (GLfloat)(r * sin(((72.0 * (GLfloat)i + rotate) / 360.0) * (2 * PI)));
	}
	// 将正五边形顺序的五个顶点对应到五角星顺序的五个顶点(0, 1, 2, 3, 4) -> (0, 2, 4, 1, 3)
	for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
		int k = j % 5;
		pentagram_vertex[i][1] = tmp[k][0];
		pentagram_vertex[i][2] = tmp[k][1];
	}
	// 五角星的边相交得到中间的小五边形顶点,计算以便绘图时的着色
	GLfloat x[4], y[4];
	for (int i = 0; i < 5; i++) {
		int v[4];
		for (int j = 0; j < 4; j++)
			v[j] = (i + j) % 5;
		for (int j = 0; j < 4; j++)
			x[j] = pentagram_vertex[v[j]][1], y[j] = pentagram_vertex[v[j]][2];
		tmp[i][0] = ((x[2] - x[3]) * (x[1] * y[0] - x[0] * y[1])
			- (x[0] - x[1]) * (x[3] * y[2] - x[2] * y[3])) /
			((x[2] - x[3]) * (y[0] - y[1]) - (x[0] - x[1]) * (y[2] - y[3]));
		tmp[i][1] = ((y[2] - y[3]) * (y[1] * x[0] - y[0] * x[1])
			- (y[0] - y[1]) * (y[3] * x[2] - y[2] * x[3])) /
			((y[2] - y[3]) * (x[0] - x[1]) - (y[0] - y[1]) * (x[2] - x[3]));
	}
	for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
		int k = j % 5;
		pentagon_vertex[i][1] = tmp[k][0];
		pentagon_vertex[i][2] = tmp[k][1];
	}
	for (unsigned int i = 0; i < 5; i++) {
		pentagram_vertex[i][0] = 0.0f;
		pentagon_vertex[i][0] = 0.0f;
	}
}

void pentagram()
{
	// 下面的段落绘制五角星中心所围成小正五边形的着色
	glColor3f(1.0f, 1.0f, 0.0f);
	glNormal3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POLYGON);
	for (unsigned int i = 0; i < 5; i++)
		glVertex3fv(pentagon_vertex[4-i]);
	glEnd();
	// 下面的段落绘制五角星的线
	glColor3f(0.0, 0.0, 0.0);
	// 设置线的宽度(0, 10]
	glLineWidth(2.5);
	glBegin(GL_LINE_LOOP);
	for (int i = 0; i < 5; i++)
		glVertex3fv(pentagram_vertex[i]);
	glEnd();
	// 下面的段落绘制五角星的五个顶点(实心圆)
	glColor3f(0.0, 0.0, 0.0);
	// 设置实心圆的半径
	GLfloat radius = 0.05f;
	// 设置用来拟合圆形的多边形边个数
	int sections = 50;
	for (int i = 0; i < 5; i++) {
		glBegin(GL_TRIANGLE_FAN);
		glVertex3fv(pentagram_vertex[i]);
		for (int j = 0; j <= sections; j++)
			glVertex3f(0.0f, (GLfloat)(pentagram_vertex[i][1] + radius * cos(j * 2.0 * PI / sections)),
				(GLfloat)(pentagram_vertex[i][2] + radius * sin(j * 2.0 * PI / sections)));
		glEnd();
	}
}

void color_cube(GLfloat size)
{
	// 设置立方体的八个顶点坐标
	static const GLfloat vertex[][3] = {
		0.0f, 0.0f, 0.0f,
		size, 0.0f, 0.0f,
		0.0f, size, 0.0f,
		size, size, 0.0f,
		0.0f, 0.0f, size,
		size, 0.0f, size,
		0.0f, size, size,
		size, size, size
	};
	// 设置绘制六个面时顶点的顺序
	static const GLint index[][4] = {
		0, 2, 3, 1,
		0, 4, 6, 2,
		0, 1, 5, 4,
		4, 5, 7, 6,
		1, 3, 7, 5,
		2, 6, 7, 3
	};
	// 指定六个面的法向
	static const GLfloat normal[][3] = {
		0.0f, 0.0f, -1.0f,
		-1.0f, 0.0f, 0.0f,
		0.0f, -1.0f, 0.0f,
		0.0f, 0.0f, 1.0f,
		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f
	};
	// 绘制六个面
	glBegin(GL_QUADS);
	for (unsigned int i = 0; i < 6; i++) {
		glNormal3fv(normal[i]);
		for (unsigned int j = 0; j < 4; j++) {
			// 每个顶点的RGB颜色值和其顶点位置坐标一致
			glColor3f(vertex[index[i][j]][0] / size,
				vertex[index[i][j]][1] / size, vertex[index[i][j]][2] / size);
			glVertex3fv(vertex[index[i][j]]);
		}
	}
	glEnd();
}

void cube_shadow(GLfloat x, GLfloat y, GLfloat z, GLfloat size)
{

	GLfloat m[16] = { 0.0f };
	m[0] = m[5] = m[10] = 1.0f;
	m[11] = -1.0f / z;
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(x, y, z);
	glMultMatrixf(m);
	glTranslatef(-x, -y, -z);
	color_cube(size);
	glPopMatrix();
}

void display()
{
	// 设置背景为白色
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// 加载单位阵
	glLoadIdentity();
	// 设置相机的位置和视角
	gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
	// 绘制坐标架以及XOY平面
	frame();
	// 平移坐标系,让三个待绘制物品的底部排列在X轴上
	glTranslatef(0.0f, 0.0f, 1.0f);
	// 在中心绘制三维彩色立方体,并绘制简单阴影
	glTranslatef(-1.0f, -1.0f, -1.0f);
	color_cube(2.0f);
	cube_shadow(0.0f, 0.0f, 10.0f, 1.5f);
	glTranslatef(1.0f, 1.0f, 1.0f);
	// 在左侧(Y轴负半轴)绘制红色球面
	glTranslatef(0.0f, -4.0f, 0.0f);
	sphere();
	glTranslatef(0.0f, 4.0f, 0.0f);
	// 在右侧(Y轴正半轴)绘制平面的五角星
	glTranslatef(0.0f, 4.0f, 0.0f);
	pentagram();
	glTranslatef(0.0f, -4.0f, 0.0f);
	// 刷新帧缓存
	glutSwapBuffers();
}

// 窗口大小自适应函数,使得窗口大小改变时仍保持图形的比例不变
// 有关窗口自适应函数:http://blog.sina.com.cn/s/blog_5497dc110102w8qh.html
void reshape(int w, int h)
{
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
}

void init()
{
	// 计算五角星有关数据
	get_pentagram();
	// 设置逆时针排列的点围成的平面为正面
	glFrontFace(GL_CCW);
	// 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);
	// 启用抗锯齿(使线平滑)
	glEnable(GL_BLEND);
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	// 设置材质和光照的信息
	// 有关光照与材质:https://blog.csdn.net/timidsmile/article/details/7017197
	GLfloat mat_ambient[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
	GLfloat mat_diffuse[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
	GLfloat mat_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat mat_shininess[4] = { 100.0f };
	GLfloat light_ambient[4] = { 0.2f, 0.2f, 0.2f, 0.0f };
	GLfloat light_diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat light_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat light_position[4] = { 0.0f, 6.0f, 0.0f, 1.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);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	// 设置颜色材料,使光照模式下仍然可以显示原本的颜色
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);
	// 启用平滑着色功能
	glShadeModel(GL_SMOOTH);
	// 启用光照功能
	glEnable(GL_LIGHTING);
	// 启用0号光源
	glEnable(GL_LIGHT0);
	// 启用检测深度
	glEnable(GL_DEPTH_TEST);
	// 环境颜色设置为白色
	glClearColor(1.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	// 设置双缓冲和RGB颜色模式
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	// 设置窗口大小、位置和名称
	glutInitWindowSize(1000, 500);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("sphere");
	init();
	// 设置绘制函数、窗口大小自适应函数
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	// 进入主循环
	glutMainLoop();
	return 0;
}
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用OpenGL绘制金字塔并进行缩放、光照和纹理,可以按照以下步骤进行: 1. 定义金字塔的顶点坐标和法向量,并将它们存储在顶点缓冲区。 2. 使用OpenGL的透视投影矩阵将3D场景投影到2D屏幕上,可以使用gluPerspective函数来实现。 3. 使用glMatrixMode函数将矩阵模式设置为GL_MODELVIEW,然后使用glLoadIdentity函数将当前矩阵重置为单位矩阵。 4. 使用glTranslatef函数将金字塔移动到屏幕心位置。 5. 使用glRotatef函数对金字塔进行旋转,可以通过修改旋转角度和轴向来实现不同的旋转效果。 6. 使用glScalef函数对金字塔进行缩放,可以通过修改缩放因子来实现不同的缩放效果。 7. 使用glEnable(GL_LIGHTING)函数启用光照,并使用glShadeModel(GL_SMOOTH)函数设置光照模式为平滑模式。 8. 使用glEnable(GL_LIGHT0)函数启用光源0,并设置光源0的位置和颜色。 9. 在渲染循环使用glClear函数清空屏幕,并使用glBindTexture函数绑定纹理,然后使用glDrawArrays函数绘制金字塔。 以下是一个简单的OpenGL程序示例,用于绘制一个带有缩放、光照和纹理的金字塔: ```C++ #include <GL/glut.h> #include <SOIL/SOIL.h> GLuint texture; GLfloat angle = 0.0; GLfloat scale = 1.0; void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluPerspective(60, 1, 0.1, 100); glTranslatef(0, 0, -5); glRotatef(angle, 0, 1, 0); glScalef(scale, scale, scale); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); GLfloat light_position[] = { -1.0, 1.0, -1.0, 0.0 }; GLfloat light_color[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_TRIANGLES); // 绘制金字塔 glEnd(); glFlush(); } void init() { // 加载纹理 texture = SOIL_load_OGL_texture("texture.png", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y); glBindTexture(GL_TEXTURE_2D, texture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); } void idle() { angle += 0.5; if (angle > 360) angle -= 360; scale += 0.01; if (scale > 1.5) scale = 1.0; glutPostRedisplay(); } int main(int argc, char * argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(400, 400); glutCreateWindow("Pyramid"); glutDisplayFunc(display); glutIdleFunc(idle); init(); glutMainLoop(); return 0; } ``` 在上面的示例,我们使用gluPerspective函数设置透视投影矩阵,glTranslatef函数将金字塔移动到屏幕心位置,glRotatef函数对金字塔进行旋转,glScalef函数对金字塔进行缩放。在渲染循环,我们使用glClear函数清空屏幕,并使用glBindTexture函数绑定纹理,然后使用glDrawArrays函数绘制金字塔。我们还使用glEnable(GL_LIGHTING)函数启用光照使用glEnable(GL_LIGHT0)函数启用光源0,并设置光源0的位置和颜色。在idle函数,我们修改旋转角度和缩放因子,以实现动态效果

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值