目录

坐标系统简介

z缓冲

举个栗子

代码

main.cpp

vertexSource.txt

fragmentSource.txt

截图


参考:LearnOpenGL

坐标系统简介

坐标变换为标准化设备坐标(Normalized Device Coordinate, NDC),接着再转化为屏幕坐标的过程通常是分步进行的,也就是类似于流水线那样子。在流水线中,物体的顶点在最终转化为屏幕坐标之前还会被变换到多个坐标系统(Coordinate System)。将物体的坐标变换到几个过渡坐标系(Intermediate Coordinate System)的优点在于,在这些特定的坐标系统中,一些操作或运算更加方便和容易,这一点很快就会变得很明显。对我们来说比较重要的总共有5个不同的坐标系统:

  • 局部空间(Local Space,或者称为物体空间(Object Space))
  • 世界空间(World Space)
  • 观察空间(View Space,或者称为视觉空间(Eye Space))
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)

为了将坐标从一个坐标系变换到另一个坐标系,我们需要用到几个变换矩阵,最重要的几个分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。

OpenGL-坐标系统,进入3D世界(深度测试)_3d 来源于LearnOpenGL
  1. 局部坐标是对象相对于局部原点的坐标,也是物体起始的坐标。
  2. 下一步是将局部坐标变换为世界空间坐标,世界空间坐标是处于一个更大的空间范围的。这些坐标相对于世界的全局原点,它们会和其它物体一起相对于世界的原点进行摆放。
  3. 接下来我们将世界坐标变换为观察空间坐标,使得每个坐标都是从摄像机或者说观察者的角度进行观察的。
  4. 坐标到达观察空间之后,我们需要将其投影到裁剪坐标。裁剪坐标会被处理至-1.0到1.0的范围内,并判断哪些顶点将会出现在屏幕上。
  5. 最后,我们将裁剪坐标变换为屏幕坐标,我们将使用一个叫做视口变换(Viewport Transform)的过程。视口变换将位于-1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。

接下来采用透视投影,即人眼所感到的近大远小。

 

z缓冲

接下来我们将画多个立方体,问题来了,如果多个立方体有重叠部分,按照我们视觉来说,前面的物体,应该遮挡住后面的物体。

那么在OpenGL中该如何实现呢?

OpenGL存储它的所有深度信息于一个Z缓冲(Z-buffer)中,也被称为深度缓冲(Depth Buffer)。GLFW会自动为你生成这样一个缓冲(就像它也有一个颜色缓冲来存储输出图像的颜色)。深度值存储在每个片段里面(作为片段的z值),当片段想要输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较,如果当前的片段在其它片段之后,它将会被丢弃,否则将会覆盖。这个过程称为深度测试(Depth Testing),它是由OpenGL自动完成的。

使用下面代码开启深度测试

glEnable(GL_DEPTH_TEST);

使用glclear函数进行清除

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
举个栗子

代码

相对于上篇文章,我们修改了main.cpp(画了6个面,添加了深度测试)、vertexSource.txt(教程没有给颜色,去除了Color)、fragmentSource.txt(教程没有给颜色,去除了Color)

main.cpp

//头文件
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//函数声明
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// 设置窗体宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

//主函数
int main()
{
	// glfw: 初始化和配置
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
#endif
	// glfw 窗体生成
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "rectwithtex", NULL, NULL);//设置标题
	//判断窗体是否生成
	if (window == NULL)
	{
		std::cout << "生成GLFW窗口失败" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// glad: 加载所有的OpenGL功能指针----------------------------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "初始化GLAD失败" << std::endl;
		return -1;
	}
	//开启深度测试
	glEnable(GL_DEPTH_TEST);
	// 建立并编译着色器--------------------------------------------------------------------
	Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");

	// 设置点数据 (还有缓冲) 配置点的属性(包含点坐标等) 这里设置了4个,将以索引的方式选择点来画三角形
	float vertices[] = {
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
		0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
		0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
	};
	unsigned int indices[] = {  
		0, 1, 2,  // 第一个三角形选择索引为 0 1 3的三个点
		2, 3, 0,  // 第一个三角形选择索引为 1 2 3的三个点
	};
	unsigned int VBO, VAO, EBO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);  //注意,这里使用EBO作为缓冲对象
	// 绑定顶点数组, 然后绑定并设置缓冲, 最后配置顶点属性-------------------------------
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
	//修改属性-----------------------------------------------------------------------------
	glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(6);
	//glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	//glEnableVertexAttribArray(7);
	glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(8);
	//纹理-------------------------------------------------------------------------------
	unsigned int TexBufferA, TexBufferB;
	stbi_set_flip_vertically_on_load(true);//y轴翻转
	//木箱部分
	glGenTextures(1, &TexBufferA);
	//激活
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, TexBufferA);
	/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
	glDrawArrays(GL_TRIANGLES, 0, 36);
	glBindTexture(GL_TEXTURE_2D, TexBufferA);//绑定
	int width, height, nrChannels;
	unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理
	}
	else
	{
		std::cout << "加载纹理失败" << std::endl;
	}
	stbi_image_free(data);//释放
	//笑脸部分
	glGenTextures(1, &TexBufferB);
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, TexBufferB);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
	glBindTexture(GL_TEXTURE_2D, TexBufferB);//绑定
	unsigned char *data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);//注意,有Alpha通道
		glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理
	}
	else
	{
		std::cout << "加载纹理失败" << std::endl;
	}
	stbi_image_free(data2);

	//注意这是允许的,对glVertexAttribPointer的调用将VBO注册为顶点属性的绑定顶点缓冲区对象,所以之后我们可以安全地解除绑定
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	// 记住:当VAO处于活动状态时,不要取消绑定EBO,因为绑定元素缓冲对象IS存储在VAO中; 保持EBO的约束力。
	//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	// 您可以在之后取消绑定VAO,以便其他VAO调用不会意外地修改此VAO,但这种情况很少发生。无论如何, 
	// 修改其他VAO需要调用glBindVertexArray,因此我们通常不会在不直接需要时解除VAO(VBO同样)的绑定。
	glBindVertexArray(0);

	// 取消注释此调用会绘制线框多边形。
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	//glm相关------------------------------------------------------------------
	// 下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
	// 下面这行代码就需要改为:
	// glm::mat4 trans = glm::mat4(1.0f)
	glm::mat4 trans;
	//trans = glm::translate(trans, glm::vec3(1.0f, 0.0f, 0.0f));
	//trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));
	//trans = glm::scale(trans, glm::vec3(1.30, 1.30, 1.30));
	glm::mat4 modelMat;
	//modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	
	glm::mat4 viewMat;
	// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
	viewMat = glm::translate(viewMat, glm::vec3(0.0f, 0.0f, -3.0f));
	glm::mat4 projMat;
	projMat = glm::perspective(glm::radians(45.0f),800.0f / 600.0f,0.1f, 100.0f);//(透视投影,远小近大)


	while (!glfwWindowShouldClose(window))
	{
		//随时间旋转
		modelMat = glm::rotate(modelMat, (float)glfwGetTime() * glm::radians(1.0f), glm::vec3(0.5f, 1.0f, 0.0f));
		// 输入
		processInput(window);
		Sleep(200);
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		//清除颜色及深度测试
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//绑定纹理
		glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
		glBindTexture(GL_TEXTURE_2D, TexBufferA);
		glActiveTexture(GL_TEXTURE3); // 在绑定纹理之前先激活纹理单元
		glBindTexture(GL_TEXTURE_2D, TexBufferB);
		glBindVertexArray(VAO);
		/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
		glDrawArrays(GL_TRIANGLES, 0, 36);
		//使用着色器
		myShader->use();
		glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0); // 手动设置
		//glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 3); // 手动设置
		myShader->setInt("ourFace", 3); // 或者使用着色器类设置
		/*unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");*/
		/*glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));*/
		unsigned int modelMatLoc = glGetUniformLocation(myShader->ID, "modelMat");
		unsigned int viewMatLoc = glGetUniformLocation(myShader->ID, "viewMat");
		unsigned int projMatLoc = glGetUniformLocation(myShader->ID, "projMat");
		glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
		glUniformMatrix4fv(viewMatLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
		glUniformMatrix4fv(projMatLoc, 1, GL_FALSE, glm::value_ptr(projMat));


		// 画矩形--------------------------------------------------------------------------
		//可以知道我们只有一个三角形VAO,没必要每次都绑定它,但是我们这么做会让代码有一点组织性
		glBindVertexArray(VAO);
		//glDrawArrays(GL_TRIANGLES, 0, 6);
		/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
		glDrawArrays(GL_TRIANGLES, 0, 36);
		// glBindVertexArray(0); //没必要每次都解绑 
		// 交换buffers和poll的IO事件 (按键按下/释放,鼠标移动等.)
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	//一旦他们超出已有的资源,就取消所有资源的分配:
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);
	// glfw:终止,清空之前所有的GLFW的预分配资源
	glfwTerminate();
	return 0;
}
//查询GLFW相关按键是否被按下/释放,根据情况作出反应
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}
// g无论窗口大小何时改变(由操作系统或用户自己)这个回调函数将会被执行
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	//确定viewport与新的窗口尺寸匹配; 请注意,宽度和高度将明显大于显示器上指定的宽度和高度。
	glViewport(0, 0, width, height);
}

vertexSource.txt

#version 330 core
layout (location = 6) in vec3 aPos;
//layout (location = 7) in vec3 aColor;
layout (location = 8) in vec2 aTexCoord;
//out vec4 vertexColor;
out vec2 TexCoord;

uniform mat4 transform;

uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;

void main()
{
   //gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);
   gl_Position = projMat *  viewMat  * modelMat * vec4(aPos.x, aPos.y, aPos.z, 1.0);
   //vertexColor = vec4(aColor.x, aColor.y, aColor.z, 1.0);
   TexCoord = aTexCoord;
}

fragmentSource.txt

#version 330 core
out vec4 FragColor;
//in vec4 vertexColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D ourFace;
void main()
{
   //FragColor = vertexColor;
   FragColor = mix(texture(ourTexture, TexCoord), texture(ourFace, TexCoord), 0.6);
}								

截图

OpenGL-坐标系统,进入3D世界(深度测试)_坐标系统_02 结果截图

注:随时间增大,旋转速度加快,电脑性能不好请及时关闭程序,免得变成暖宝宝。

更多OpenGL知识:现代OpenGL入门教程

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。