深圳大学计算机图形学期末大作业——OpenGL glfw库实现简单的Minecraft游戏

源代码:

OpenGL大作业OpenCraft-其他文档类资源-CSDN下载 

目录

1. OBJ文件读取

2. 物体渲染与纹理着色。

3. 相机变换的实现

 4. 光照与阴影的实现

5. 阴影设计

6. 层级建模

 7.添加动画

8. 交互

        1.视角移动

        2. 鼠标左右键添加删除物体

        3. 选择移动、添加的物体

        4. 光源位置改变

        5. 层级建模交互

 9. 其余功能

        1. 视角跟随机器人模式

         2. 光标制作


1. OBJ文件读取

我们先看部分OBJ文件内容。

# material
mtllib Ground.mtl
usemtl palette

# normals
vn -1 0 0
vn 1 0 0
vn 0 0 1
vn 0 0 -1
vn 0 -1 0
vn 0 1 0

# texcoords
vt 0.00195313 0.5
vt 0.00585938 0.5
vt 0.00976563 0.5
vt 0.0136719 0.5
vt 0.0175781 0.5
vt 0.0214844 0.5
vt 0.0253906 0.5


# verts
v -50 0 50
v -50 0 49
v -50 0 48
v -50 0 47
v -50 0 46
v -50 0 45
v -50 0 44
v -50 0 43
v -50 0 42
v -50 0 41
v -50 0 40


# faces
f 101/20/1 2/20/1 1/20/1
f 102/41/1 3/41/1 2/41/1
f 102/20/1 2/20/1 101/20/1
f 103/1/1 4/1/1 3/1/1
f 103/41/1 3/41/1 102/41/1
f 104/2/1 5/2/1 4/2/1
f 104/1/1 4/1/1 103/1/1
f 105/48/1 6/48/1 5/48/1
f 105/2/1 5/2/1 104/2/1
f 106/12/1 7/12/1 6/12/1
f 106/48/1 6/48/1 105/48/1

        我们看到在每一行的前面都有字母,v代表的是每个点的位置,vn代表的是每个点的法向量,vt代表的是纹理图片的坐标,f是每个面片的信息,由三组(或者四组)以斜杠分隔的整数表示该面片第 i 个顶点的 位置索引/纹理坐标索引/法向量索引

        读取时我么需要将里面的数据一次放在vertex_positions, vertex_normals, vertex_textures, faces, texture_index, noemal_index 当中。

    while (std::getline(fin, line))
	{
		std::istringstream sin(line);
		std::string type;
		GLfloat _x, _y, _z;
		int a0, b0, c0;
		int a1, b1, c1;
		int a2, b2, c2;
		char slash;
		// 读取obj文件,记录里面的这些数据
		sin >> type;
		glm::vec3 tmp_node;
		if (type == "v")
		{
			sin >> tmp_node.x >> tmp_node.y >> tmp_node.z;
			vertex_positions.push_back(tmp_node);
			if (MinPosition > tmp_node.z)
				MinPosition = tmp_node.z;
		}
		if (type == "vn")
		{
			sin >> tmp_node.x >> tmp_node.y >> tmp_node.z;
			vertex_normals.push_back(tmp_node);
			vertex_colors.push_back(tmp_node);
		}
		if (type == "vt")
		{
			float x, y, z;
			sin >> x >> y >> z;
			vertex_textures.push_back(glm::vec2(x, y));
		}

		if (type == "f")
		{
			sin >> a0 >> slash >> b0 >> slash >> c0;
			sin >> a1 >> slash >> b1 >> slash >> c1;
			sin >> a2 >> slash >> b2 >> slash >> c2;
			faces.push_back(vec3i(a0 - 1, a1 - 1, a2 - 1));
			texture_index.push_back(vec3i(b0 - 1, b1 - 1, b2 - 1));
			color_index.push_back(vec3i(c0 - 1, c1 - 1, c2 - 1));
			normal_index.push_back(vec3i(c0 - 1, c1 - 1, c2 - 1));
		}

		// 其中vertex_color和color_index可以用法向量的数值赋值
	}

        此时已经将所有的点的信息传递给了数组当中。

        接下来将根据面片顶点坐标,依次加入GPU points等容器中。

        在此之前,先将物体的大小进行了归一化处理,也就是让所有的物体尺寸处于同一大小。这个可以通过setNormalize函数进行控制,我们看看若没有进行归一化是怎样的效果。

        可以看到wawa的模型十分大,直接包围了table模型。

        所以说归一化的操作是十分有必要的,让两个物体大小相差不大。

        归一化代码如下:

    if (do_normalize_size)
	{
		// 记录物体包围盒大小,可以用于大小的归一化
		// 先获得包围盒的对角顶点
		float max_x = -FLT_MAX;
		float max_y = -FLT_MAX;
		float max_z = -FLT_MAX;
		float min_x = FLT_MAX;
		float min_y = FLT_MAX;
		float min_z = FLT_MAX;
		for (int i = 0; i < vertex_positions.size(); i++)
		{
			auto &position = vertex_positions[i];
			if (position.x > max_x)
				max_x = position.x;
			if (position.y > max_y)
				max_y = position.y;
			if (position.z > max_z)
				max_z = position.z;
			if (position.x < min_x)
				min_x = position.x;
			if (position.y < min_y)
				min_y = position.y;
			if (position.z < min_z)
				min_z = position.z;
		}
		up_corner = glm::vec3(max_x, max_y, max_z);
		down_corner = glm::vec3(min_x, min_y, min_z);
		center = glm::vec3((min_x + max_x) / 2.0, (min_y + max_y) / 2.0, (min_z + max_z) / 2.0);

		diagonal_length = length(up_corner - down_corner);

		minz = FLT_MAX; //找到最低的点
		minx = FLT_MAX;
		miny = FLT_MAX;

		for (int i = 0; i < vertex_positions.size(); i++)
		{
			vertex_positions[i] = (vertex_positions[i] - center) / diagonal_length;
			if (minz > vertex_positions[i].z)
				minz = vertex_positions[i].z;
			if (miny > vertex_positions[i].y)
				miny = vertex_positions[i].y;
			if (minx > vertex_positions[i].x)
				minx = vertex_positions[i].x;
		}

	}

        然后将点信息放进GPU存储器当中。

    for (int i = 0; i < faces.size(); i++)
		{
			// 坐标
			points.push_back(vertex_positions[faces[i].x]);
			points.push_back(vertex_positions[faces[i].y]);
			points.push_back(vertex_positions[faces[i].z]);
			// 颜色
			colors.push_back(vertex_colors[color_index[i].x]);
			colors.push_back(vertex_colors[color_index[i].y]);
			colors.push_back(vertex_colors[color_index[i].z]);
			// 法向量
			normals.push_back(vertex_normals[normal_index[i].x]);
			normals.push_back(vertex_normals[normal_index[i].y]);
			normals.push_back(vertex_normals[normal_index[i].z]);
			// 纹理
			textures.push_back(vertex_textures[texture_index[i].x]);
			textures.push_back(vertex_textures[texture_index[i].y]);
			textures.push_back(vertex_textures[texture_index[i].z]);
		}

这样子所有的模型存储在GPU当中,准备进行建模。

2. 物体渲染与纹理着色。

        在main函数中,我编写了一个addMeshes函数,调用这个函数就可以将物体加入到painter当中,使用着色器进行进一步的渲染。

void addMeshes(glm::vec3 Translation, glm::vec3 Rotation, bool setNormalize, std::string Name,
			   std::string OBJLocation, std::string TextureLocation, glm::vec3 Scall = glm::vec3(1.0, 1.0, 1.0))
{
	std::string vshader, fshader;
	// 读取着色器并使用

	// for Windows
	vshader = "shaders/vshader_win.glsl";
	fshader = "shaders/fshader_win.glsl";

	TriMesh *TMesh = new TriMesh();
	TMesh->setNormalize(true);
	TMesh->readObj(OBJLocation);

	// 设置物体的旋转位移

	TMesh->setRotation(Rotation);
	TMesh->setScale(Scall);
	TMesh->setTranslation(Translation + glm::vec3(0.0, -(TMesh->miny * Scall.y), 0.0)); //使物体始终处于地面上方

	TMesh->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0));  // 环境光
	TMesh->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0));  // 漫反射
	TMesh->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	TMesh->setShininess(1.0);						   //高光系数
	// 加到painter中
	painter->addMesh(TMesh, Name, TextureLocation, vshader, fshader); // 指定纹理与着色器
}

        该函数当中,只需要输入物体初始状态的位置、角度、比例、物体名称、是否需要将物体归一化和物体文件地址和纹理地址。在函数中,调用TriMesh创建一个物体,设置其位置大小比例,再设置其光反射系数。最后将设置好的物体加入到painter当中,调用painter的addMesh函数,准备开始着色。

        

void MeshPainter::addMesh(TriMesh *mesh, const std::string &name, const std::string &texture_image,
                          const std::string &vshader, const std::string &fshader)
{
    mesh_names.push_back(name);
    meshes.push_back(mesh);

    openGLObject object;

    bindObjectAndData(mesh, object, texture_image, vshader, fshader);

    opengl_objects.push_back(object);
};

        在addMesh函数当中,首先将物体存入到meshes数组当中,所有产生的单一物体都会存放在meshes函数当中,以便之后的渲染,接着调用bindObjectAndData函数,将物体调用到着色器当中,准备渲染。

        接下来,若要增加物体,直接在main文件当中调用addMeshes函数即可。

        最后在display函数中,调用painter的drawMeshes函数便可实现物体在窗口当中显示。

void MeshPainter::drawMeshes(Light *light, Camera *camera)
{
    drawMesh(meshes[0], opengl_objects[0], light, camera, meshes[0]->getModelMatrix(), 0);
    //地面不需要阴影
    for (int i = 1; i < meshes.size(); i++)
    {
        drawMesh(meshes[i], opengl_objects[i], light, camera, meshes[i]->getModelMatrix());
    }
};
void MeshPainter::drawMesh(TriMesh *mesh, openGLObject &object,
                           Light *light, Camera *camera, glm::mat4 modelMatrix)
{

    // 相机矩阵计算
    camera->updateCamera();
    camera->viewMatrix = camera->getViewMatrix();
    camera->projMatrix = camera->getProjectionMatrix(true);

#ifdef __APPLE__ // for MacOS
    glBindVertexArrayAPPLE(object.vao);
#else
    glBindVertexArray(object.vao);
#endif
    glUseProgram(object.program);

    // 物体的变换矩阵

    // 传递矩阵
    glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
    glUniformMatrix4fv(object.viewLocation, 1, GL_TRUE, &camera->viewMatrix[0][0]);
    glUniformMatrix4fv(object.projectionLocation, 1, GL_TRUE, &camera->projMatrix[0][0]);
    // 将着色器 isShadow 设置为0,表示正常绘制的颜色,如果是1着表示阴影
    glUniform1i(object.shadowLocation, 0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, object.texture); // 该语句必须,否则将只使用同一个纹理进行绘制
    // 传递纹理数据 将生成的纹理传给shader
    glUniform1i(glGetUniformLocation(object.program, "texture"), 0);

    // 将材质和光源数据传递给着色器
    bindLightAndMaterial(mesh, object, light, camera);
    // 绘制
    glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());

#ifdef __APPLE__ // for MacOS
    glBindVertexArrayAPPLE(0);
#else
    glBindVertexArray(0);
#endif
    glUseProgram(0);
}

经过上述操作,便可实现物体在窗口当中显示。

实现纹理的着色是使用glBindTexture函数,只需要将相应的点与纹理图片,即可使物体附着上纹理。

  • 10
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
实验一 OpenGL+GLUT开发平台搭建 5 小实验1: 开发环境设置 5 小实验2: 控制窗口位置和大小 6 小实验3:默认的可视化范围 6 小实验4:自定义可视化范围 7 小实验5: 几何对象变形的原因 8 小实验6: 视口坐标系及视口定义 8 小实验7:动态调整长宽比例,保证几何对象不变形 9 实验二 动画和交互 10 小实验1: 单缓冲动画技术 10 小实验2: 双缓冲动画技术 11 小实验3:键盘控制 13 小实验4:鼠标控制【试着单击鼠标左键或者右键,试着按下鼠标左键后再移动】 14 实验三 几何变换、观察变换、三维对象 16 小实验1:二维几何变换 16 小实验2:建模观察(MODELVIEW)矩阵堆栈 17 小实验3:正平行投影1 19 小实验4:正平行投影2 19 小实验5:正平行投影3 20 小实验6:透射投影1 21 小实验6:透射投影2 22 小实验7:三维对象 24 实验四 光照模型和纹理映射 26 小实验1:光照模型1----OpenGL简单光照效果的关键步骤。 26 小实验2:光照模型2----光源位置的问题 28 小实验3:光照模型3----光源位置的问题 31 小实验4:光照模型4----光源位置的问题 33 小实验5:光照模型5----光源位置的问题 35 小实验6:光照模型6----光源位置的问题 38 小实验7:光照模型7----光源位置的动态变化 40 小实验8:光照模型8----光源位置的动态变化 43 小实验9:光照模型9---光源位置的动态变化 45 小实验10:光照模型10---聚光灯效果模拟 48 小实验11:光照模型11---多光源效果模拟 50 小实验12:光照效果和雾效果的结合 53 小实验13:纹理映射初步—掌握OpenGL纹理映射的一般步骤 56 小实验13:纹理映射—纹理坐标的自动生成(基于参数的曲面映射) 59 小实验14:纹理映射—纹理坐标的自动生成(基于参考面距离) 61

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anakin Skywalker RM 00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值