您当前的位置: 首页 >  ar

龚建波

暂无认证

  • 3浏览

    0关注

    312博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

LearnOpenGL学习笔记:着色器

龚建波 发布时间:2019-11-11 13:43:04 ,浏览量:3

( 本文对应学习章节:https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/ )

0.前言

在之前绘制三角形的章节中已经初步了解了着色器和着色器语言。在本章节中,教程进一步讲解着色器和着色器语言GLSL,及相关的数据类型,参数传递等。在最后,封装了一个简易的着色器类,简化主体流程。

1.了解着色器与着色器语言

着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。

着色器是使用一种叫GLSL的类C语言写成的,典型的着色器代码结构如下:

#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;

uniform type uniform_name;

int main()
{
  // 处理输入并进行一些图形操作
  ...
  // 输出处理过的结果到输出变量
  out_variable_name = weird_stuff_we_processed;
}
数据类型

GLSL中包含常见的基础数据类型:int、float、double、uint和bool。也有两种容器类型:向量(Vector)和矩阵(Matrix)。

向量是一个可以包含至多4个分量的容器,可以看作基本类型的复合形式:vec3就是3个分量的float向量(首字母不带类型默认是float),bvec2表示2个分量的bool向量,ivec4表示4个分量的int向量。

对于向量中的分量,可以通过.xyzw或是.rgba或是.stpq来访问,而且可以灵活的重组和构造:

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);
输入与输出

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

顶点着色器从顶点数据中直接接收输入。为了指定输入变量,需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。而layout的location偏移是通过glVertexAttribPointer函数的第一个参数指定的:

glVertexAttribPointer定义通用顶点属性数据的数组:

void glVertexAttribPointer(GLuint index​, GLint size​, GLenum type​, GLboolean normalized​, GLsizei stride​, const GLvoid * pointer​);

参数1指定顶点属性的索引;参数2指定顶点属性的分量数,可以是[1-4];参数3指定数据类型;参数4设置是否归一化;参数5指定连续的顶点属性间的字节偏移;参数6指定第一个值的偏移量。可以对照上面的代码示例。

另一个例外是片段着色器,它需要一个vec4颜色输出变量 ,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

Uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

假设我们在片段着色器声明了一个uniform的颜色向量:

uniform vec4 ourColor;

 我们可以通过着色器程序对象来动态设置这个颜色:

int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUniform4f(vertexColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);

glGetUniformLocation找到uniform变量的位置, glUniform指定当前程序对象的uniform变量的值。glGetUniformLocation返回-1就表示没有找到这个变量。glUniform函数名后面有分量数及类型,如glUniform4f就表示需要4个float作为它的值。

2.封装一个简易的着色器类

代码Github地址:https://github.com/gongjianbo/LearnTheOpenGL

整体比较简单,就是把着色器的初始化流程封装了下,以及对uniform变量操作的封装。教程是读取文件来初始化类的,我为了方便直接使用的字符串。

#include 
#include 

#include 
#include 

class MyShader
{
public:
	//根据着色器代码来初始化
	MyShader(const std::string& vertexCode, const std::string& fragmentCode)
	{
		init(vertexCode, fragmentCode);
	}
	//激活程序
	void useProgram()
	{
		glUseProgram(programID);
	}
	// uniform工具函数
	void setInt(const std::string& name, int value)
	{
		glUniform1i(glGetUniformLocation(programID, name.c_str()), value);
	}
	void setFloat(const std::string& name, float value)
	{
		glUniform1f(glGetUniformLocation(programID, name.c_str()), value);
	}

private:
	void init(const std::string& vertexCode, const std::string& fragmentCode)
	{
		const char* vertex_shader_code = vertexCode.c_str();
		const char* fragment_shader_code = fragmentCode.c_str();

		//顶点着色器
		unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex_shader, 1, &vertex_shader_code, NULL);
		glCompileShader(vertex_shader);
		checkError(vertex_shader, "vertex");

		//片段着色器
		unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment_shader, 1, &fragment_shader_code, NULL);
		glCompileShader(fragment_shader);
		checkError(fragment_shader,"fragment");

		//着色器程序
		programID = glCreateProgram();
		glAttachShader(programID, vertex_shader);
		glAttachShader(programID, fragment_shader);
		glLinkProgram(programID);
		checkError(programID, "program");

		// 删除着色器,它们已经链接到我们的程序中了
		glDeleteShader(vertex_shader);
		glDeleteShader(fragment_shader);
	}

	void checkError(unsigned int name,const std::string &type)
	{
		int check_flag; 
		char check_info[1024];
		if (type != "program") {
			glGetShaderiv(name, GL_COMPILE_STATUS, &check_flag);
			if (!check_flag) {
				glGetShaderInfoLog(name, 1024, NULL, check_info);
				std::cout             
关注
打赏
1655829268
查看更多评论
0.0490s