OpenGL学习——第5天(着色器)

学习来源:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

还是老话吧,为了学过就忘,准备写点东西,或者摘抄点东西。

 

注:本文所有代码教程都有,若是看不惯本文的可以直接去教程里找。

(出现任何差错的,欢迎在评论区与我讨论!)

目录

GLSL

数据类型

向量

向量的重组(Swizzling)

输入和输出

Uniform

更多属性

我们自己的着色器类

从文件读取


着色器是一种把输入转化为输出的程序。

GLSL

√GLSL:OpenGL着色器语言。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

着色器的开头总是要声明版本,接着是输入输出变量uniformmain函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。

谈论顶点着色器的时候,每个输入变量也叫顶点属性(Vertex Attribute)

 

数据类型

默认基础数据类型:int、float、double、uint和bool。

两种容器类型:向量(Vector)和矩阵(Matrix)。

 

向量

GLSL中的向量是一个可以包含有1、2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。

类型

含义

vecn

包含n个float分量的默认向量

bvecn

包含n个bool分量的向量

ivecn

包含n个int分量的向量

uvecn

包含n个unsigned int分量的量

dvecn

包含n个double分量的向量

 

大多数时候我们使用vecn,因为float足够满足大多数要求了。

可以分别使用vec.x、.y、.z和.w来获取向量的第1、2、3、4个分量。

向量的重组(Swizzling)

允许:

vec2 someVec;

vec4 differentVec = someVec.xyxx;

vec3 anotherVec = differentVec.zyw;

vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

vec2 vect = vec2(0.5, 0.7);

vec4 result = vec4(vect, 0.0, 0.0);

vec4 otherResult = vec4(result.xyz, 1.0);

 

输入和输出

每个着色器使用这inout关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点片段着色器中会有点不同。

√①顶点着色器接收一种特殊形式输入,否则就会效率低下。使用location这一元数据指定输入变量,这样才可以在CPU上配置顶点属性:layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据

 

√②片段着色器,它需要一个vec4颜色输出变量。从一个着色器向另一个着色器发送数据的时候,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。

 

改动之前“一个三角形”的程序。

顶点着色器

#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0

out vec4 vertexColor; // 为片段着色器指定一个颜色输出
void main(){
    gl_Position = vec4(aPos, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 把输出变量设置为暗红色
}

片段着色器

#version 330 core
out vec4 FragColor;

in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
void main(){
    FragColor = vertexColor;
}

顶点着色器中声明了一个vertexColor变量作为vec4输出,并在片段着色器中声明了一个类似的vertexColor。由于它们名字相同且类型相同,片段着色器中的vertexColor就和顶点着色器中的vertexColor链接了。

 

我们在“一个三角形”的代码基础上更改代码的一部分,更改如下:

(“一个三角形”代码见我上一篇文章:OpenGL学习——第4天(绘制三角形)

代码在文末https://blog.csdn.net/weixin_40851250/article/details/85038950 

这边的所有代码往下翻一翻就好了)

/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor; // 为片段着色器指定一个颜色输出\n"/*添加*/
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0); "// 注意我们如何把一个vec3作为vec4的构造器的参数
"   vertexColor = vec4(0.5, 0.0, 0.0, 1.0);"// 把输出变量设置为暗红色
"}\0";

/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"// 从顶点着色器传来的输入变量(名称相同、类型相同)
"void main()\n"
"{\n"
"   FragColor = vertexColor;\n"//作了更改
"}\n\0";

/*着色器源码*/
//const char *vertexShaderSource = "#version 330 core\n"
//"layout (location = 0) in vec3 aPos;\n"
//"void main()\n"
//"{\n"
//"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//"}\0";


///*片段着色器源码*/
//const char *fragmentShaderSource = "#version 330 core\n"
//"out vec4 FragColor;\n"
//"void main()\n"
//"{\n"
//"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//"}\n\0";

更改后三角形变了颜色:

我们来看一下颜色变化:原来的代码中,FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);,(1.0f,0.5f,0.2f)就是rgb的配色,末尾1.0f是不透明度。后来颜色变成了:vertexColor = vec4(0.5, 0.0, 0.0, 1.0),也就是我们的暗红色。过程:在着色器中新建了一个vertexColorvec4变量,然后将其传给片段着色器使用。

只要我们改变这个变量,比如:我们将vertexColor = vec4(0.5, 0.0, 0.0, 1.0);改为vertexColor = vec4(1.0, 0.7, 0.6, 1.0);,就看到三角形变成了肉粉色。

到这里,我们成功地从顶点着色器向片段着色器发送数据。

代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/

/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor; // 为片段着色器指定一个颜色输出\n"/*添加*/
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0); "// 注意我们如何把一个vec3作为vec4的构造器的参数
"   vertexColor = vec4(1.0, 0.7, 0.6, 1.0);"// 把输出变量设置为暗红色
"}\0";

/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"// 从顶点着色器传来的输入变量(名称相同、类型相同)
"void main()\n"
"{\n"
"   FragColor = vertexColor;\n"//作了更改
"}\n\0";

/*着色器源码*/
//const char *vertexShaderSource = "#version 330 core\n"
//"layout (location = 0) in vec3 aPos;\n"
//"void main()\n"
//"{\n"
//"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//"}\0";


///*片段着色器源码*/
//const char *fragmentShaderSource = "#version 330 core\n"
//"out vec4 FragColor;\n"
//"void main()\n"
//"{\n"
//"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//"}\n\0";

void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/

/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
		glfwSetWindowShouldClose(window, true);
}

/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}


int main()
{
	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); // Mac OS系统就加上这一句
#endif

	/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

	/*如果创建窗口失败*/
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
	glfwMakeContextCurrent(window);

	/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
	/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


	/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
	int vertexShader = glCreateShader(GL_VERTEX_SHADER);
	/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	/*然后编译它*/
	glCompileShader(vertexShader);

	/*检测在调用glCompileShader后编译是否成功*/
	int success;
	char infoLog[512];/*储存错误消息(如果有的话)的容器*/
	/*用glGetShaderiv检查是否编译成功*/
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
	int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*用glCreateProgram建一个程序对象*/
	int shaderProgram = glCreateProgram();

	/*把之前编译的着色器附加到程序对象上*/
	glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
	glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/

	/*然后用glLinkProgram链接它们*/
	glLinkProgram(shaderProgram);

	/*检测链接着色器程序是否失败,并获取相应的日志*/
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}

	/*把着色器对象链接到程序对象以后,删除着色器对象*/
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	
	/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
	float vertices[] = {
		-0.5f, -0.5f, 0.0f, // left  
		 0.5f, -0.5f, 0.0f, // right 
		 0.0f,  0.5f, 0.0f  // top   
	};

	//float vertices[] = {
	//	 0.5f,  0.5f, 0.0f,  // top right
	//	 0.5f, -0.5f, 0.0f,  // bottom right
	//	-0.5f, -0.5f, 0.0f,  // bottom left
	//	-0.5f,  0.5f, 0.0f   // top left 
	//};
	//unsigned int indices[] = {  // note that we start from 0!
	//	0, 1, 3,  // first Triangle
	//	1, 2, 3   // second Triangle
	//};

	/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	/*使用glBindVertexArray绑定VAO*/
	glBindVertexArray(VAO);

	/*把顶点数组复制到缓冲中供OpenGL使用*/
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	/*glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
	第一个参数是目标缓冲的类型
	第二个参数指定传输数据的大小(以字节为单位)
	第三个参数是我们希望发送的实际数据
	第四个参数指定了我们希望显卡如何管理给定的数据*/
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


	//unsigned int VBO, VAO, EBO;/*多了一个EBO*/
	//glGenVertexArrays(1, &VAO);
	//glGenBuffers(1, &VBO);
	//glGenBuffers(1, &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);/*添加*/


	/*设置顶点属性指针*/
	/*第一个参数指定我们要配置的顶点属性,第二个参数指定顶点属性的大小,第三个参数指定数据的类型
	下个参数定义我们是否希望数据被标准化(Normalize),第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔
	最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	/*通过glVertexAttribPointer设置的顶点属性配置。*/
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	/*渲染循环*/
	while (!glfwWindowShouldClose(window))
	{
		/*迭代调用processInput输入函数*/
		processInput(window);

		/*每个新的渲染迭代开始的时候清屏*/
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		/*画三角形*/

		/*通过glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)函数配置OpenGL如何绘制图元
		第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
		之后的绘制调用会一直以线框模式绘制三角形,直到我们
		用glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)将其设置回默认模式*/
		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);/*线框模式*/

		glUseProgram(shaderProgram);/*程序对象*/
		glBindVertexArray(VAO); 
		/*glDrawArrays函数,它使用当前激活的着色器,之前定义的顶点属性配置,
		和VBO的顶点数据(通过VAO间接绑定)来绘制图元
		glDrawArrays函数第一个参数是我们打算绘制的OpenGL图元的类型*/

		glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/
		//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);/*绘制两个三角形时*/


		/*检查并调用事件,交换缓冲*/
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	//glDeleteBuffers(1, &EBO);/*添加*/

	/*调用glfwTerminate函数来完成*/
	glfwTerminate();
	return 0;
}

Uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同:uniform是全局的(Global)

声明一个GLSL的uniform:在一个着色器中添加uniform关键字至类型和变量名前。

如果声明了一个uniform却在GLSL代码中没用过,编译器会静默移除这个变量,导致最后编译出的版本中并不会包含它,这可能导致几个非常麻烦的错误

这个uniform现在还是空的;我们还没有给它添加任何数据,首先需要找到着色器中uniform属性索引/位置值,然后更新它的值,让它随着时间改变颜色。

接下来可能就是唯一需要看的地方了,那就是Uniform使用之后的区别。

 

修改地点①:着色器源码(修改的地方用不同的颜色标志了)

/*着色器源码*/

const char *vertexShaderSource = "#version 330 core\n"

"layout (location = 0) in vec3 aPos;\n"

"void main()\n"

"{\n"

"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"

"}\0";

/*着色器源码*/

const char *vertexShaderSource = "#version 330 core\n"

"layout (location = 0) in vec3 aPos;\n"

"void main()\n"

"{\n"

"   gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)

"}\0";

/*片段着色器源码*/

const char *fragmentShaderSource = "#version 330 core\n"

"out vec4 FragColor;\n"

"void main()\n"

"{\n"

"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"

"}\n\0";

/*片段着色器源码*/

const char *fragmentShaderSource = "#version 330 core\n"

"out vec4 FragColor;\n"

"uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor

"void main()\n"

"{\n"                        

"   FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f)

"}\n\0";

修改地点②:while循环

/*激活着色器*/

glUseProgram(shaderProgram);

 

/*更新uniform颜色*/

float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/

float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/

/*用glGetUniformLocation查询uniform ourColor的位置值*/

int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");

 

/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/

glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/

/*通过glUniform4f函数设置uniform值。*/

glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);/*后面四位是rgba*/

 

最后三角形由绿,再又绿,循环往复,代码就是正确的了。

一点小改变:在上面的代码中,我们可以看到这一句:

glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

我加的注释里也写明了,后面四位就是控制颜色与透明度的,greenValue是我的一个随时间变化的float变量,我们如果把这句话改成:

glUniform4f(vertexColorLocation, 1.0f-greenValue, greenValue, 1.0f, 1.0f);

三角形颜色就会由,再由

代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/

/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"}\0";


/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor
"void main()\n"
"{\n"                        //并把片段着色器的输出颜色设置为uniform值的内容
"   FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f)
"}\n\0";

void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/

/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
		glfwSetWindowShouldClose(window, true);
}

/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}


int main()
{
	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); // Mac OS系统就加上这一句
#endif

	/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

	/*如果创建窗口失败*/
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
	glfwMakeContextCurrent(window);

	/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
	/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


	/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
	int vertexShader = glCreateShader(GL_VERTEX_SHADER);
	/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	/*然后编译它*/
	glCompileShader(vertexShader);

	/*检测在调用glCompileShader后编译是否成功*/
	int success;
	char infoLog[512];/*储存错误消息(如果有的话)的容器*/
	/*用glGetShaderiv检查是否编译成功*/
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
	int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*用glCreateProgram建一个程序对象*/
	int shaderProgram = glCreateProgram();

	/*把之前编译的着色器附加到程序对象上*/
	glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
	glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/

	/*然后用glLinkProgram链接它们*/
	glLinkProgram(shaderProgram);

	/*检测链接着色器程序是否失败,并获取相应的日志*/
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}

	/*把着色器对象链接到程序对象以后,删除着色器对象*/
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	
	/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
	float vertices[] = {
		-0.5f, -0.5f, 0.0f, // left  
		 0.5f, -0.5f, 0.0f, // right 
		 0.0f,  0.5f, 0.0f  // top   
	};

	/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	/*使用glBindVertexArray绑定VAO*/
	glBindVertexArray(VAO);

	/*把顶点数组复制到缓冲中供OpenGL使用*/
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	/*glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
	第一个参数是目标缓冲的类型
	第二个参数指定传输数据的大小(以字节为单位)
	第三个参数是我们希望发送的实际数据
	第四个参数指定了我们希望显卡如何管理给定的数据*/
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	/*设置顶点属性指针*/
	/*第一个参数指定我们要配置的顶点属性,第二个参数指定顶点属性的大小,第三个参数指定数据的类型
	下个参数定义我们是否希望数据被标准化(Normalize),第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔
	最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	/*通过glVertexAttribPointer设置的顶点属性配置。*/
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	/*渲染循环*/
	while (!glfwWindowShouldClose(window))
	{
		/*迭代调用processInput输入函数*/
		processInput(window);

		/*每个新的渲染迭代开始的时候清屏*/
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		/*激活着色器*/
		glUseProgram(shaderProgram);

		/*更新uniform颜色*/
		float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/
		float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/
		/*用glGetUniformLocation查询uniform ourColor的位置值*/
		int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");

		/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/
		glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/
		/*通过glUniform4f函数设置uniform值。*/
		glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);/*后面四位是rgba*/
		
		/*绘制三角形*/
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/

		/*交换缓冲并查询IO事件*/
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	//glDeleteBuffers(1, &EBO);/*添加*/

	/*调用glfwTerminate函数来完成*/
	glfwTerminate();
	return 0;
}

更多属性

在前面的教程中,我们了解了如何填充VBO配置顶点属性指针以及如何把它们储存到一个VAO里。

把颜色数据加进顶点数据中:我们将把颜色数据添加为3个float值至vertices数组,把三角形的三个角分别指定为红色绿色蓝色

√修改地点①

float vertices[] = {
    // 位置              // 颜色
     0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
     0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
};

 

由于现在有更多的数据要发送到顶点着色器,所以要调整一下顶点着色器,使它能够接收颜色值作为一个顶点属性输入。用layout标识符来把aColor属性的位置值设置为1。

√修改地点②:顶点着色器

/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"}\0";

/*顶点着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"// 位置变量的属性位置值为 0 
"layout (location = 1) in vec3 aColor;\n"// 颜色变量的属性位置值为 1
"out vec3 ourColor; \n"// 向片段着色器输出一个颜色
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"   ourColor=aColor;\n"//将ourColor设置为我们从顶点数据那里得到的输入颜色
"}\0";

由于我们不再使用uniform来传递片段的颜色了,现在使用ourColor输出变量,我们必须再修改一下片段着色器

√修改地点③:片段着色器源码

/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor
"void main()\n"
"{\n"                        //并把片段着色器的输出颜色设置为uniform值的内容
"   FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f)
"}\n\0";

/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"                        
"   FragColor = vec4(ourColor, 1.0);\n"
"}\n\0";

因为我们添加了另一个顶点属性,并且更新VBO的内存,我们就必须重新配置顶点属性指针。使用glVertexAttribPointer函数更新顶点格式。

√修改地点④:属性指针配置。(while循环上面的4行)

/*设置顶点属性指针*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	/*通过glVertexAttribPointer设置的顶点属性配置。*/
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

/*位置属性*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	/*通过glVertexAttribPointer设置的顶点属性配置。*/
	glEnableVertexAttribArray(0);
	/*颜色属性*/
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	glUseProgram(shaderProgram);

√修改地点⑤:while循环里删除uniform相关的那一段:

/*激活着色器*/
		glUseProgram(shaderProgram);

		/*更新uniform颜色*/
		float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/
		float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/
		/*用glGetUniformLocation查询uniform ourColor的位置值*/
		int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");

		/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/
		glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/
		/*通过glUniform4f函数设置uniform值。*/
		glUniform4f(vertexColorLocation, 1.0f-greenValue, greenValue, 1.0f, 1.0f);/*后面四位是rgba*/

最后会出现一个这样的彩色三角形

全部代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/

/*顶点着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"// 位置变量的属性位置值为 0 
"layout (location = 1) in vec3 aColor;\n"// 颜色变量的属性位置值为 1
"out vec3 ourColor;\n"// 向片段着色器输出一个颜色
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"   ourColor=aColor;\n"//将ourColor设置为我们从顶点数据那里得到的输入颜色
"}\0";


/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"                        
"   FragColor = vec4(ourColor, 1.0f);\n"
"}\n\0";

void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/

/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
		glfwSetWindowShouldClose(window, true);
}

/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}


int main()
{
	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); // Mac OS系统就加上这一句
#endif

	/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

	/*如果创建窗口失败*/
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
	glfwMakeContextCurrent(window);

	/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
	/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


	/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
	int vertexShader = glCreateShader(GL_VERTEX_SHADER);
	/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	/*然后编译它*/
	glCompileShader(vertexShader);

	/*检测在调用glCompileShader后编译是否成功*/
	int success;
	char infoLog[512];/*储存错误消息(如果有的话)的容器*/
	/*用glGetShaderiv检查是否编译成功*/
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
	int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
	}


	/*用glCreateProgram建一个程序对象*/
	int shaderProgram = glCreateProgram();

	/*把之前编译的着色器附加到程序对象上*/
	glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
	glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/

	/*然后用glLinkProgram链接它们*/
	glLinkProgram(shaderProgram);

	/*检测链接着色器程序是否失败,并获取相应的日志*/
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}

	/*把着色器对象链接到程序对象以后,删除着色器对象*/
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

	
	/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
	float vertices[] = {
		// 位置              // 颜色
		 0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
		-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
		 0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
	};

	/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	/*使用glBindVertexArray绑定VAO*/
	glBindVertexArray(VAO);

	/*把顶点数组复制到缓冲中供OpenGL使用*/
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	/*位置属性*/
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	/*通过glVertexAttribPointer设置的顶点属性配置。*/
	glEnableVertexAttribArray(0);

	/*颜色属性*/
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	glUseProgram(shaderProgram);

	/*渲染循环*/
	while (!glfwWindowShouldClose(window))
	{
		/*迭代调用processInput输入函数*/
		processInput(window);

		/*每个新的渲染迭代开始的时候清屏*/
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
	
		
		/*绘制三角形*/
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/

		/*交换缓冲并查询IO事件*/
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	//glDeleteBuffers(1, &EBO);/*添加*/

	/*调用glfwTerminate函数来完成*/
	glfwTerminate();
	return 0;
}

我们自己的着色器类

自己写着色器显得很麻烦,这是总有英雄拯救世界——封装

把着色器类全部放在在头文件里,添加必要的include,并定义类结构(具体见教程):

#ifndef SHADER_H#define SHADER_H
#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件
#include <string>#include <fstream>#include <sstream>#include <iostream>

class Shader
{public:
    // 程序ID
    unsigned int ID;

    // 构造器读取并构建着色器
    Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
    // 使用/激活程序
    void use();
    // uniform工具函数
    void setBool(const std::string &name, bool value) const;  
    void setInt(const std::string &name, int value) const;   
    void setFloat(const std::string &name, float value) const;
};
#endif

从文件读取

①配置GLM

OpenGL GLM 环境配置

http://www.pianshen.com/article/130332661/

 

②配置文件路径

在D盘根目录下(随便哪里都可以,反正到时候路径写正确就好了),新建两个txt文件,然后分别写入(两个着色器的源码):

#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec3 aColor;
out vec3 ourColor;
void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}

上面这个保存为:3.3.shader.vs   (随便什么名字,到时候自己传参的时候要一致)

另一个如下:

#version 330 coreout vec4 FragColor;
in vec3 ourColor;
void main()
{
    FragColor = vec4(ourColor, 1.0f);
}

这个保存为:3.3.shader.fs(随便什么名字,到时候自己传参的时候要一致)

新建一个名为shader_s.h的头文件,代码(教程也有)如下:

#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h>
#include <glm/glm.hpp>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
	unsigned int ID;
	// constructor generates the shader on the fly
	// ------------------------------------------------------------------------
	Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
	{
		// 1. retrieve the vertex/fragment source code from filePath
		std::string vertexCode;
		std::string fragmentCode;
		std::string geometryCode;
		std::ifstream vShaderFile;
		std::ifstream fShaderFile;
		std::ifstream gShaderFile;
		// ensure ifstream objects can throw exceptions:
		vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
		fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
		gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
		try
		{
			// open files
			vShaderFile.open(vertexPath);
			fShaderFile.open(fragmentPath);
			std::stringstream vShaderStream, fShaderStream;
			// read file's buffer contents into streams
			vShaderStream << vShaderFile.rdbuf();
			fShaderStream << fShaderFile.rdbuf();
			// close file handlers
			vShaderFile.close();
			fShaderFile.close();
			// convert stream into string
			vertexCode = vShaderStream.str();
			fragmentCode = fShaderStream.str();
			// if geometry shader path is present, also load a geometry shader
			if (geometryPath != nullptr)
			{
				gShaderFile.open(geometryPath);
				std::stringstream gShaderStream;
				gShaderStream << gShaderFile.rdbuf();
				gShaderFile.close();
				geometryCode = gShaderStream.str();
			}
		}
		catch (std::ifstream::failure e)
		{
			std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
		}
		const char* vShaderCode = vertexCode.c_str();
		const char * fShaderCode = fragmentCode.c_str();
		// 2. compile shaders
		unsigned int vertex, fragment;
		// vertex shader
		vertex = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex, 1, &vShaderCode, NULL);
		glCompileShader(vertex);
		checkCompileErrors(vertex, "VERTEX");
		// fragment Shader
		fragment = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment, 1, &fShaderCode, NULL);
		glCompileShader(fragment);
		checkCompileErrors(fragment, "FRAGMENT");
		// if geometry shader is given, compile geometry shader
		unsigned int geometry;
		if (geometryPath != nullptr)
		{
			const char * gShaderCode = geometryCode.c_str();
			geometry = glCreateShader(GL_GEOMETRY_SHADER);
			glShaderSource(geometry, 1, &gShaderCode, NULL);
			glCompileShader(geometry);
			checkCompileErrors(geometry, "GEOMETRY");
		}
		// shader Program
		ID = glCreateProgram();
		glAttachShader(ID, vertex);
		glAttachShader(ID, fragment);
		if (geometryPath != nullptr)
			glAttachShader(ID, geometry);
		glLinkProgram(ID);
		checkCompileErrors(ID, "PROGRAM");
		// delete the shaders as they're linked into our program now and no longer necessery
		glDeleteShader(vertex);
		glDeleteShader(fragment);
		if (geometryPath != nullptr)
			glDeleteShader(geometry);

	}
	// activate the shader
	// ------------------------------------------------------------------------
	void use()
	{
		glUseProgram(ID);
	}
	// utility uniform functions
	// ------------------------------------------------------------------------
	void setBool(const std::string &name, bool value) const
	{
		glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
	}
	// ------------------------------------------------------------------------
	void setInt(const std::string &name, int value) const
	{
		glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
	}
	// ------------------------------------------------------------------------
	void setFloat(const std::string &name, float value) const
	{
		glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
	}
	// ------------------------------------------------------------------------
	void setVec2(const std::string &name, const glm::vec2 &value) const
	{
		glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
	}
	void setVec2(const std::string &name, float x, float y) const
	{
		glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
	}
	// ------------------------------------------------------------------------
	void setVec3(const std::string &name, const glm::vec3 &value) const
	{
		glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
	}
	void setVec3(const std::string &name, float x, float y, float z) const
	{
		glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
	}
	// ------------------------------------------------------------------------
	void setVec4(const std::string &name, const glm::vec4 &value) const
	{
		glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
	}
	void setVec4(const std::string &name, float x, float y, float z, float w)
	{
		glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
	}
	// ------------------------------------------------------------------------
	void setMat2(const std::string &name, const glm::mat2 &mat) const
	{
		glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}
	// ------------------------------------------------------------------------
	void setMat3(const std::string &name, const glm::mat3 &mat) const
	{
		glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}
	// ------------------------------------------------------------------------
	void setMat4(const std::string &name, const glm::mat4 &mat) const
	{
		glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}

private:
	// utility function for checking shader compilation/linking errors.
	// ------------------------------------------------------------------------
	void checkCompileErrors(GLuint shader, std::string type)
	{
		GLint success;
		GLchar infoLog[1024];
		if (type != "PROGRAM")
		{
			glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
			if (!success)
			{
				glGetShaderInfoLog(shader, 1024, NULL, infoLog);
				std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
			}
		}
		else
		{
			glGetProgramiv(shader, GL_LINK_STATUS, &success);
			if (!success)
			{
				glGetProgramInfoLog(shader, 1024, NULL, infoLog);
				std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
			}
		}
	}
};
#endif

然后是我们的cpp代码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include "shader_s.h"

#include <iostream>

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}


int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);


	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	Shader ourShader("D:\\3.3.shader.vs", "D:\\3.3.shader.fs"); 

	float vertices[] = {
		// positions         // colors
		 0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right
		-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left
		 0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top 
	};

	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glBindVertexArray(VAO);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	while (!glfwWindowShouldClose(window))
	{

		processInput(window);

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		ourShader.use();
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glfwTerminate();
	return 0;
}

区别:

我们可以看到,两个着色器源码都用外部文件(那两个vs与fs文件)替代了。

中间非常冗长的一段(float之前)都被封装了,然后使用

Shader ourShader("D:\\3.3.shader.vs", "D:\\3.3.shader.fs"); 调用。最后还是一个彩色的三角形。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值