OpenGL系列教程(二)---旋转的立方体

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011283226/article/details/85077161

【写在前面】

本章主要内容:

1、OpenGL Texture(OpenGL纹理)

2、Vertex Array Object(顶点数组对象)


【正文开始】

相比上一章,本章并没有多少新的内容,所以我们直接进入关键的MyRender类中:

#ifndef MYRENDER_H
#define MYRENDER_H
#include "OpenGLRender.h"
#include <glm/matrix.hpp>
#include <glm/gtc/quaternion.hpp>

class MyRender : public OpenGLRender
{
public:
	MyRender();
	~MyRender();

	void render(const glm::quat &rotation);
	void resizeGL(int w, int h);
	void initializeGL();
	void initializeShader();
	void initializeTexture();
	void initializeCube();

private:
	GLuint m_vao;
	GLuint m_vbo;
	GLuint m_ebo;
	GLuint m_texture1, m_texture2;
	GLuint m_program;
	glm::mat4x4 m_projection;
};

#endif

这里将要讲的是顶点数组对象(Vertex Array Object, VAO):

它可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中。

当我们使用OpenGL的核心模式(Core)时,必须使用顶点数组对象,否则OpenGL不会绘制任何东西

一个VAO会存储以下内容:

 

具体怎么使用我们来看代码:

void MyRender::initializeCube()
{
	VertexData vertices[] =
	{
		{ glm::vec3( -0.75f, -0.75f,  0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2( 0.0f, 0.0f) },
		{ glm::vec3(  0.75f, -0.75f,  0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2( 1.0f, 0.0f) },
		{ glm::vec3(-0.375f,  0.75f, 0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.33f, 1.0f) },
		{ glm::vec3( 0.375f,  0.75f, 0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(0.66f, 1.0f) },

		{ glm::vec3( 0.75f, -0.75f,   0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2( 0.0f, 0.0f) },
		{ glm::vec3( 0.75f, -0.75f,  -0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2( 1.0f, 0.0f) },
		{ glm::vec3(0.375f,  0.75f,  0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.33f, 1.0f) },
		{ glm::vec3(0.375f,  0.75f, -0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(0.66f, 1.0f) },

		{ glm::vec3(  0.75f, -0.75f,  -0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2( 0.0f, 0.0f) },
		{ glm::vec3( -0.75f, -0.75f,  -0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2( 1.0f, 0.0f) },
		{ glm::vec3( 0.375f,  0.75f, -0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.33f, 1.0f) },
		{ glm::vec3(-0.375f,  0.75f, -0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(0.66f, 1.0f) },

		{ glm::vec3( -0.75f, -0.75f,  -0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2( 0.0f, 0.0f) },
		{ glm::vec3( -0.75f, -0.75f,   0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2( 1.0f, 0.0f) },
		{ glm::vec3(-0.375f,  0.75f, -0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.33f, 1.0f) },
		{ glm::vec3(-0.375f,  0.75f,  0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(0.66f, 1.0f) },

		{ glm::vec3(-0.375f, 0.75f,  0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.0f, 0.0f) },
		{ glm::vec3( 0.375f, 0.75f,  0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f) },
		{ glm::vec3(-0.375f, 0.75f, -0.375f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(0.0f, 1.0f) },
		{ glm::vec3( 0.375f, 0.75f, -0.375f), glm::vec3(0.8f, 0.0f, 0.0f), glm::vec2(1.0f, 1.0f) },

		{ glm::vec3( 0.75f, -0.75f,  0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2(0.0f, 0.0f) },
		{ glm::vec3(-0.75f, -0.75f,  0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(1.0f, 0.0f) },
		{ glm::vec3( 0.75f, -0.75f, -0.75f), glm::vec3(0.8f, 0.8f, 0.0f), glm::vec2(0.0f, 1.0f) },
		{ glm::vec3(-0.75f, -0.75f, -0.75f), glm::vec3(0.0f, 0.8f, 0.8f), glm::vec2(1.0f, 1.0f) }
	};

	GLushort indices[] =
	{
		 0,  1,  2,  3,  3,
		 4,  4,  5,  6,  7, 7,
		 8,  8,  9, 10, 11, 11,
		12, 12, 13, 14, 15, 15,
		16, 16, 17, 18, 19, 19,
		20, 20, 21, 22, 23
	};

	glGenVertexArrays(1, &m_vao);
	glBindVertexArray(m_vao);

	glGenBuffers(1, &m_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glGenBuffers(1, &m_ebo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	int location = 0;
	glVertexAttribPointer(location, 3, GL_FLOAT, GL_TRUE, sizeof(VertexData), (void *)0);
	glEnableVertexAttribArray(location);
	glVertexAttribPointer(location + 1, 3, GL_FLOAT, GL_TRUE, sizeof(VertexData), (void *)(sizeof(glm::vec3)));
	glEnableVertexAttribArray(location + 1);
	glVertexAttribPointer(location + 2, 2, GL_FLOAT, GL_TRUE, sizeof(VertexData), (void *)(sizeof(glm::vec3) * 2));
	glEnableVertexAttribArray(location + 2);
}

1、使用glGenVertexArrays()生成一个VAO。

2、使用glBindVertexArray()绑定到当前激活的VAO。

接着我们生成VBO和EBO,分别将顶点和顶点索引缓存。

索引缓存对象(Element Array Object, EBO)】,它的作用和VBO一样,不同的是它缓存的是顶点索引,使用EBO,绘制命令为glDrawElements()。

 接下来我们需要生成纹理(Image类是读取图像的):

void MyRender::initializeTexture()
{
	glGenTextures(1, &m_texture1);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, m_texture1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	Image image1("../container.jpg");
	if (image1.data())
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image1.width(), image1.height(),
			0, GL_RGB, GL_UNSIGNED_BYTE, image1.data());
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else std::cout << "Open Image1 Error! ";

	glGenTextures(1, &m_texture2);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, m_texture2);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	Image image2("../face.jpg", true);
	if (image2.data())
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image2.width(), image2.height(),
			0, GL_RGB, GL_UNSIGNED_BYTE, image2.data());
		glGenerateMipmap(GL_TEXTURE_2D);
	}
	else std::cout << "Open Image2 Error! ";
}

1、使用glGenTextures()生成一个纹理对象。

2、使用glBindTexture()将其绑定到当前激活的纹理目标上,目标可以是:GL_TEXTURE_1D(sampler1D)、GL_TEXTURE_1D_ARRAY(sampler1DArray)、GL_TEXTURE_2D(sampler2D)、GL_TEXTURE_2D_ARRAY(sampler2DArray)、GL_TEXTURE_MULTISAMPLE(sampler2DMS)、GL_TEXTURE_MULTISAMPLE(sampler2DMSArray)、GL_TEXTURE_3D(sampler3D)、GL_TEXTURE_CUBE(samplerCube)、GL_TEXTURE_ARRAY(samplerCubeArray)、GL_TEXTURE_RECTANGLE(samplerRect)、GL_TEXTURE_BUFFER(samplerBuffer),其中括号里面的是片元着色器的采样器类型。

3、使用glTexImage2D()为纹理分配可变的内存(不可变的使用glTexStorage2D())。

4、使用glGenerateMipmap()生成层级映射,这步是可选的但推荐使用。

5、使用glTexParameteri()为纹理设置环绕方式和放大/缩小滤波器。

因为本章使用了两个纹理,所以在这里生成了两个。

然后,来看render():

void MyRender::render(const glm::quat &rotation)
{
	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//标准示例
	glm::mat4 modelMatrix(1.0f);
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0f, 0.0f, -5.0f));
	modelMatrix *= glm::toMat4(rotation);
	
	glUseProgram(m_program);
	GLuint mvp = glGetUniformLocation(m_program, "mvp");
	glUniformMatrix4fv(mvp, 1, GL_FALSE, glm::value_ptr(m_projection * modelMatrix));

	glBindVertexArray(m_vao);
	glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr);
}

render()基本就很简单了,我们使用glm::quat(四元数)进行旋转,然后设置顶点着色器中的mvp矩阵,最后绑定VAO并发出绘制命令。

四元数是表达一个旋转的一种方式】,相关的文章有很多。

 接着是顶点着色器:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color0;
layout (location = 2) in vec2 texCoord0;

uniform mat4 mvp;
out vec3 color;
out vec2 texCoord;

void main()
{
	gl_Position = mvp * vec4(position, 1.0);
	color = color0;
	texCoord = texCoord0;
}

它的工作也很简单,将position变换后输出,color0和texCoord0直接输出(我们的VertexData包含一个顶点,一个颜色,一个2D纹理坐标)

纹理坐标(Texture Coordinate)】,用来标明该从纹理图像的哪个部分采样(采集片段颜色)。之后在图形的其它片段上进行片段插值。

 最后是片元着色器:

#version 330 core

in vec2 texCoord;
in vec3 color;
out vec4 fragColor;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main(void)
{
	vec4 tempColor = mix(texture2D(texture1, texCoord), texture2D(texture2, texCoord), 0.1);
	//fragColor = tempColor * vec4(color, 1.0);
	fragColor = tempColor;
}

因为我们使用了两个纹理,所以这里是两个sampler2D采样器。

sampler*D是不透明类型,所以只能使用uniform来定义它】,其他不透明类型还有image(图像)、atomic counter(原子计数器)。

texture1的location默认为0(对应纹理单元GL_TEXTURE0),texture2为1(对应纹理单元GL_TEXTURE1)。

 接着使用mix()将采样到的颜色进行混合,texture2D()函数从给定的纹理和坐标进行采样。

注释掉的一行则使用了color,有兴趣的可以自己试试。


【结语】

好了,这一章就讲解完了。。不过我还是没有讲得太仔细。。所以可能需要你自己多试试才能明白,然后还有一些其他的东西,具体可以自己看代码了解。

附上系列代码地址:https://github.com/mengps/OpenGL-Totural/

猜你喜欢

转载自blog.csdn.net/u011283226/article/details/85077161
今日推荐