【OpenGL】蓝宝书第六章学习笔记——跳出“盒子”:非存储着色器

OpenGL着色语言(GLSL)

类似C语言,由OpenGL实现进行编译和连接,并且是完全在图形硬件中运行。至少需要两个着色器:顶点着色器和片段着色器,可选的是几何着色器(第十一章介绍)。

可选三种方式向顶点着色器传递数据:①参数,针对每个顶点而言的;②统一值,是针对整个顶点数据批次的常量(所以针对每个顶点是一致的);③纹理数据。可为片段着色器设置统一值和纹理数据。

将顶点属性发送到片段着色器毫无意义,因为片段着色器只是用来在图元进行光栅化后对片段进行填充的。不过,每个顶点数据都可通过顶点程序传递到片段着色器,这些数据可能是常量,也可能是通过一些方式插值得到的值。

着色器程序和C语言一样从main函数开始,并且使用同样的字符集和注释约定,以及很多相同的处理指令。可在OpenGL着色语言规范中找到一个完整的语言规范。学习本章需掌握C/C++基础知识。

变量和数据类型

整数(包括有符号和无符号)、浮点数(OpenGL3.3只能使用单精度)、布尔值,注意:没有字符串或字符以及指针,允许有void类型,但同样不允许void指针类型。
bool bDone = false; //布尔
int iValue = 42; //有符号整数
uint uiValue = 3929u; //无符号整数
float fValue = 42.0f; //单精度浮点值

向量类型

OpenGL着色语言与C/C++相比,有一个特性即向量数据类型。所有上述介绍的四种类型都可以存储在二维、三维或者四维向量中,如下:
vec2,vec3,vec4         2分量、3分量和4分量浮点向量
ivec2,ivec3,ivec4         2分量、3分量和4分量整数向量
uvec2,uvec3,uvec4       2分量、3分量和4分量无符号整数向量
bvec2,bvec3,bvec4       2分量、3分量和4分量布尔向量
4分量的浮点向量声明一个顶点位置:vec4 vVertexPos; 或 vec4 vVertexPos = vec4(39.0f, 10.0f, 0.0f, 1.0f); 这种构造函数是不同于C++类构造函数的,它并不是类,而是有着自己的内建数据类型。
向量之间可赋值、相加,与标量相乘或除进行缩放。

OpenGL着色语言另一个特性是对一个向量的独立元素进行寻址方式。类似C/C++的union(联合体)结构,向量与联合体非常类似。

我们用点号来确定多达4个向量元素的地址,可用3组标识符中的任意一组进行表达:xyzw, rgba, stpq。位置信息一般用xyzw,颜色用rgba,纹理坐标用stpq表达。
例如,vVertexPos.x = 3.0f;    vVertexPos.xy = vec2(3.0f, 2.5f); vVertexPos.xyz = vNewPos.xyz;
操作颜色时用rgba即 vOutputColor.r = 1.0f;  vOutputColor.rgba = vec4(1.0f, 1.0f,1.0f, 1.0f);
操作纹理坐标用stpq: vTexCoord.st = vec2(1.0f, 0.0f);  
这些仅仅是规范,实际可随意使用:vTexCoord.st = vVertex.st;  但不能太随意例如:  vTexCoord.st = vVertex.xt;  其中x和t是不同组的标识符,这种情况是不允许的。
向量数据类型是硬件的本地数据类型,它们速度很快可用vVertex.xyz = vOtherVertex.xyz + vec3(5.0f, 4.0f, 1.0f); 来进行一次性赋值xyz。

矩阵类型

mat2, mat2x2  两行两列  mat3,mat3x3 三行上列 mat4,mat4x4 四行四列 mat2x3 三行两列 mat2x4四行两列 mat3x2 两行三列 mat3x4 四行三列 mat4x2 两行四列 mat4x3 三行四列

只支持浮点数。

矩阵是一个列向量数组,例如设置一个mat4x4类型的第四列可用代码: mModelView[3] = vec4(0.0f, 0.0f, 0.0f, 1.0f);  
可如下方式恢复一个矩阵的最后一列: vec4 vTranslation = mModelView[3]; 或进行细致的查询:vec3 vTranslation = mModelView[3].xyz;
可进行乘以向量,通餐在通过模型视图投影矩阵来对一个向量进行变换时使用:
vec4 vVertex;
mat4 mvpMatrix;
... ... ...
vOutPos = mvpMatrix * vVertex;

矩阵构造函数:
mat4 vTranslation = mat4(1.0f ,0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
上面是一个单位矩阵,可用mat4(1.0)来快速构建。

存储限定符

限定符用于标记变量为输入变量、输出变量或常量。输入是指从C++提交的属性或之前的着色器阶段输入的数据(如顶点传到片段着色器的)。输出指着色器的输出数据。
<none> 只是普通的本地变量,外部不可见,外部不可访问。
const  编译时常量,一个对函数来说只读的参数
in  一个从之前的阶段传递过来的变量
in centroid 一个从之前的阶段传递过来的变量,使用质心插值
out  传递到下一个处理阶段或一个函数中指定的一个返回值
out centroid 传递到下一个阶段,使用质心插值
inout 一个读/写变量, 只能用于局部函数参数
uniform 一个从客户端代码传递过来的变量,在顶点之间不做改变。【统一值】

inout限定符 只能修饰非数组形式的函数形参,代表读/写变量。
例如: int CalculateSomething(float fTime, float fStepSize, inout float fVariance);
返回整数(通过/失败标记),可修改传入的fVariance值,代码中可读取传入的fVariance值。在C++中是利用指针来达到这种inout效果,即将inout float 改为 float* 。

质心插值 centroid限定符在多重采样缓冲区不会生效,在单采样缓冲区中,插值操作总是从像素中心开始的,故名 质心插值。 对于多重采样, 在使用centroid限定符时, 插补值将会被选中,因此它会落到图元和像素中。(完全搞不懂这句话)(第九章补充解释)

smooth out vec3 vSmoothValue; //smooth默认的关键词声明,这个变量是以一种透视正确的方法进行插补的。
flat out vec3 vFlatColor; //flat声明 不进行插值
noperspective float vLinearlySmoothed; //noperspective声明 非透视插值(线性?)

真正的着色器

顶点着色器 ShadedIdentity.vp 文件代码:(文件本质是纯文本ASCII码,仅仅后缀习惯性命名为.vp 代表vertex program)

// The ShadedIdentity Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible

//指定版本 #version 330 指定OpenGL着色语言最低版本为3.3 若不支持则不会编译着色器,如下是1.3
//OpenGL4.0 对应4.0 即400
//OpenGL3.3 对应3.3 即330  从3.3版本开始 其着色语言也对应3.3版本。
//OpenGL3.2 对应1.5 即150
//OpenGL3.1 对应1.4 即140
//OpenGL3.0 对应1.3 即130
#version 130
//顶点位置属性 in修饰的属性是由外部OpenGL代码为每一个顶点传递值且该变量是只读的!
in vec4 vColor;
//顶点颜色属性
in vec4 vVertex;
//传递到片段着色器的颜色值  由顶点着色器main方法赋值 (必须在片段着色器对应一个in vec4 vVaryingColor;) 不然会出现编译出错
out vec4 vVaryingColor;  

//每个顶点都会执行一遍的代码
void main(void) 
    { 
    //注意:试图修改vColor或vVertex都会编译报错!因它们都是只读的.
    vVaryingColor = vColor; //简单复制颜色值
    gl_Position = vVertex;  //简单传递顶点位置 gl_Position是一个预定义的内建4分量向量,它必须在顶点着色器被赋值,作为一个顶点着色器的输出,它用于几何图形阶段装配图元的。我们没对坐标进行任何变换即坐标是在+/- 1.0之间的
    }

片段着色器 ShadedIdentity.fp 文件代码:(本质是纯文本ASCII码,后缀习惯命名为.fp 即fragment program) 另一种方法是将文本作为硬编码的字符数组存储在C/C++源代码中 或算法生成着色器代码,或从数据库中恢复,又或者从某种加密数据文件中恢复,但目前学习阶段可直接文本形式)

// The ShadedIdentity Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130
//输出到下一个流程的片段颜色值
out vec4 vFragColor;
//顶点着色器传入的数据 颜色值(它是一个插值值,其插值方式默认为smooth 透视正确插值,其他文章有介绍)
in vec4 vVaryingColor;

void main(void)
   { 
   //简单的进行复制颜色输出
   vFragColor = vVaryingColor;
   }
// Triangle.cpp
// Our first OpenGL program that will just draw a triangle on the screen.
#pragma comment(lib, "gltools.lib")
#include <GLTools.h>            // OpenGL toolkit
#include <GLShaderManager.h>    // Shader Manager Class

#ifdef __APPLE__
#include <glut/glut.h>          // OS X version of GLUT
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>            // Windows FreeGlut equivalent
#endif

GLBatch	triangleBatch;
GLShaderManager	shaderManager;

GLint	myIdentityShader;

///
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
	glViewport(0, 0, w, h);
}


///
// This function does any needed initialization on the rendering context. 
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
	// Blue background
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	shaderManager.InitializeStockShaders();

	// Load up a triangle
	GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
						  0.5f, 0.0f, 0.0f,
						  0.0f, 0.5f, 0.0f };

	GLfloat vColors[] = { 1.0f, 0.0f, 0.0f, 1.0f,
						   0.0f, 1.0f, 0.0f, 1.0f,
						   0.0f, 0.0f, 1.0f, 1.0f };

	triangleBatch.Begin(GL_TRIANGLES, 3);
	triangleBatch.CopyVertexData3f(vVerts);
	triangleBatch.CopyColorData4f(vColors);
	triangleBatch.End();

	myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2,
		GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");
}


///
// Cleanup
void ShutdownRC()
{
	glDeleteProgram(myIdentityShader);

}


///
// Called to draw scene
void RenderScene(void)
{
	// Clear the window with current clearing color
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	glUseProgram(myIdentityShader);
	triangleBatch.Draw();

	// Perform the buffer swap to display back buffer
	glutSwapBuffers();
}


///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
	gltSetWorkingDirectory(argv[0]);

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Shaded Triangle");
	glutReshapeFunc(ChangeSize);
	glutDisplayFunc(RenderScene);

	GLenum err = glewInit();
	if (GLEW_OK != err) {
		fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
		return 1;
	}

	SetupRC();

	glutMainLoop();

	ShutdownRC();

	return 0;
}

 函数gltLoadShaderPairWithAttributes具体代码:


/
// Load a pair of shaders, compile, and link together. Specify the complete
// source text for each shader. After the shader names, specify the number
// of attributes, followed by the index and attribute name of each attribute
GLuint gltLoadShaderPairWithAttributes(const char *szVertexProg, const char *szFragmentProg, ...)
{
	//临时着色器对象
	GLuint hVertexShader;
	GLuint hFragmentShader;
	GLuint hReturn = 0;
	GLint testVal;

	//创建着色器对象顶点和片段着色器
	hVertexShader = glCreateShader(GL_VERTEX_SHADER);
	hFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

	//加载它们 如果失败则进行清除并返回null
	// 加载顶点着色器
	if (gltLoadShaderFile(szVertexProg, hVertexShader) == false)
	{
		glDeleteShader(hVertexShader);
		glDeleteShader(hFragmentShader);
		fprintf(stderr, "The shader at %s could ot be found.\n", szVertexProg);
		return (GLuint)NULL;
	}

	// 加载片段着色器
	if (gltLoadShaderFile(szFragmentProg, hFragmentShader) == false)
	{
		glDeleteShader(hVertexShader);
		glDeleteShader(hFragmentShader);
		fprintf(stderr, "The shader at %s  could not be found.\n", szFragmentProg);
		return (GLuint)NULL;
	}

	// 顶点和片段着色器编译
	glCompileShader(hVertexShader);
	glCompileShader(hFragmentShader);
 
	// 在顶点着色器中检查错误
	glGetShaderiv(hVertexShader, GL_COMPILE_STATUS, &testVal);
	if (testVal == GL_FALSE)
	{
		char infoLog[1024];
		glGetShaderInfoLog(hVertexShader, 1024, NULL, infoLog);
		fprintf(stderr, "The shader at %s failed to compile with the following error:\n%s\n", szVertexProg, infoLog);
		glDeleteShader(hVertexShader);
		glDeleteShader(hFragmentShader);
		return (GLuint)NULL;
	}

	// 在片段着色器中检查错误
	glGetShaderiv(hFragmentShader, GL_COMPILE_STATUS, &testVal);
	if (testVal == GL_FALSE)
	{
		char infoLog[1024];
		glGetShaderInfoLog(hFragmentShader, 1024, NULL, infoLog);
		fprintf(stderr, "The shader at %s failed to compile with the following error:\n%s\n", szFragmentProg, infoLog);
		glDeleteShader(hVertexShader);
		glDeleteShader(hFragmentShader);
		return (GLuint)NULL;
	}

	// 创建最终的程序对象,并连接着色器
	hReturn = glCreateProgram();
	glAttachShader(hReturn, hVertexShader);
	glAttachShader(hReturn, hFragmentShader);


	// 现在,我们需要将参数名绑定到它们指定的参数位置列表上
	// List of attributes
	va_list attributeList;
	va_start(attributeList, szFragmentProg);

	// 重复迭代这个参数列表
	char *szNextArg;
	int iArgCount = va_arg(attributeList, int);	// Number of attributes
	for (int i = 0; i < iArgCount; i++)
	{
		int index = va_arg(attributeList, int);
		szNextArg = va_arg(attributeList, char*);
		glBindAttribLocation(hReturn, index, szNextArg);
	}
	va_end(attributeList);

	// 尝试连接
	glLinkProgram(hReturn);

	// 这些都不需要了
	glDeleteShader(hVertexShader);
	glDeleteShader(hFragmentShader);

	// 确认连接有效
	glGetProgramiv(hReturn, GL_LINK_STATUS, &testVal);
	if (testVal == GL_FALSE)
	{
		char infoLog[1024];
		glGetProgramInfoLog(hReturn, 1024, NULL, infoLog);
		fprintf(stderr, "The programs %s and %s failed to link with the following errors:\n%s\n",
			szVertexProg, szFragmentProg, infoLog);
		glDeleteProgram(hReturn);
		return (GLuint)NULL;
	}

	// All done, return our ready to use shader program
	return hReturn;
}

主要理解的是下面一行代码,SetupRC函数内进行的加载顶点和片段着色器并进行编译和连接在一起,指定了输入顶点着色器的参数类型和参数名称。
例如:第三个参数2代表顶点着色器属性数量,GLT_ATTRIBUTE_VERTEX(0)代表vVertex顶点位置属性的参数列表索引号,GLT_ATTRIBUTE_COLOR(1)代表vColor顶点颜色属性信息的的参数列表索引号。

GLint	myIdentityShader;

...
	myIdentityShader = gltLoadShaderPairWithAttributes("ShadedIdentity.vp", "ShadedIdentity.fp", 2,
		GLT_ATTRIBUTE_VERTEX, "vVertex", GLT_ATTRIBUTE_COLOR, "vColor");

可支持0~15个索引,即16个属性。
 

enum GLT_SHADER_ATTRIBUTE { GLT_ATTRIBUTE_VERTEX = 0, GLT_ATTRIBUTE_COLOR, GLT_ATTRIBUTE_NORMAL, 
                                    GLT_ATTRIBUTE_TEXTURE0, GLT_ATTRIBUTE_TEXTURE1, GLT_ATTRIBUTE_TEXTURE2, GLT_ATTRIBUTE_TEXTURE3, 
                                    GLT_ATTRIBUTE_LAST};

编译

    glCompileShader(hVertexShader);
    glCompileShader(hFragmentShader);

创建程序对象并使用它连接2个着色器

    hReturn = glCreateProgram();
    glAttachShader(hReturn, hVertexShader);
    glAttachShader(hReturn, hFragmentShader);

程序对象绑定参数列表 即以 lua表形式表达的  { [GLT_ATTRIBUTE_VERTEX] = "vVertex" , [GLT_ATTRIBUTE_COLOR] = "vColor" }
先用attributeList从着色器中获取参数列表,不知道为啥它传的是szFragmentProg是片段着色器的路径...迷?拿到后即如上信息,遍历获取索引index和参数名szNextArg 进行捆绑glBindAttributeLocation(程序对象, index, szNextArg);

// 现在,我们需要将参数名绑定到它们指定的参数位置列表上
	// List of attributes
	va_list attributeList;
	va_start(attributeList, szFragmentProg);

	// 重复迭代这个参数列表
	char *szNextArg;
	int iArgCount = va_arg(attributeList, int);	// Number of attributes
	for (int i = 0; i < iArgCount; i++)
	{
		int index = va_arg(attributeList, int);
		szNextArg = va_arg(attributeList, char*);
		glBindAttribLocation(hReturn, index, szNextArg);
	}
	va_end(attributeList);

着色器程序对象连接着色器(其实不明白这里又有一个连接是不是连的是渲染流程上的其他着色器)

	// 尝试连接
	glLinkProgram(hReturn);

	// 这些都不需要了
	glDeleteShader(hVertexShader);
	glDeleteShader(hFragmentShader);

当不使用这个返回的着色器程序对象时需要删除它 void glDeleteProgram(GLuint program);

使用着色器

glUseProgram(myShaderProgram); 这样就使用了,提交的批次图形就会通过它进行渲染。在提交顶点属性前要进行对Uniform值和纹理进行设置。

简单地通过GLTriangleBatch类进行提交批次,使用了CopyVertexData3f传递3个顶点位置 和 CopyColorData4f传递3个顶点颜色,注意的是通过GLBatch或GLTriangleBatch类进行设置的批次 在使用gltLoadShaderPairWithAttributes时必须使用GLT_SHADER_ATTRIBUTE枚举值进行指定参数索引值,不然将无法正确地对应捆绑参数,而出现问题。

接着是使用着色器并批次Draw即可完成

	glUseProgram(myIdentityShader);
	triangleBatch.Draw();

Provoking Vertex

"Provoking Vertex" 简单来说就是修改了顶点着色器的输出out vec4 vVaryingColor 原本是用smooth修饰的(默认),改为用flat修饰 即 flat out vec4 vVaryingColor ,同时片段着色器也改为flat in vec4 vVaryingColor 这样就得到了一个由最后一个顶点颜色(蓝色)填充的三角面,是的,它只会应用三角面的三个顶点最后一个输入的顶点信息来进行填充,而不会是之前smooth平滑过渡三个顶点的颜色进行填充。

可使用void glProvokingVertex(GLenum provokeMode); GL_FIRST_VERTEX_CONVENTION 和 GL_LAST_VERTEX_CONVENTIONS(默认值) 将应用值更改为用第一个顶点数据还是最后一个顶点数据。
新增代码如下,来进行按空格切换应用值。

int nToggle = 1;
void KeyPressFunc(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		nToggle++;

		if (nToggle % 2 == 0) {
			glProvokingVertex(GL_LAST_VERTEX_CONVENTION);
			glutSetWindowTitle("Provoking Vertex - Last Vertex - Press Space Bars");
		}
		else {
			glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
			glutSetWindowTitle("Provoking Vertex - First Vertex - Press Space Bars");
		}

		glutPostRedisplay();
	}
}

...
glutKeyboardFunc(KeyPressFunc); //设置键盘回调

着色器统一值uniform

uniform float fTime;
uniform int iIndex;
uniform vec4 vColorValue;
uniform mat4 mvpMatrix;
使用uniform来进行修饰变量即可,这样这个值就针对整个图元批次保持不变,普遍用于变换矩阵。

寻找统一值

着色器编译和连接完成之后,需要找到统一值位置用GLint glGetUniformLocation(GLuint shaderID, const GLchar* varName); 
shaderID: 指定着色器ID
varName: 指定统一值name
返回:统一值在shader的位置
例如:GLint iLocation = glGetUniformLocation(myShader, "vColorValue") 名称区分大小写,返回-1则说明没有找到。
如果统一值没有被使用则编译器会丢弃它。

设置标量和向量统一值

void glUniform1f(GLint location, GLfloat v0);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);

void glUniform1i(GLint location, GLint v0);
void glUniform2i(GLint location, GLint v0, GLint v1);
void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2);
void glUniform4i(GLint location, GLint v0 GLint v1, GLint v2, GLint v3);

例如在着色器声明4个变量:
uniform float fTime;
uniform int iIndex;
uniform vec4 vColorValue;
uniform bool bSomeFlag;
使用时如下:
GLint locTime, locIndex, locColor, locFlag;
locTime = glGetUniformLocation(myShader, "fTime");
locIndex= glGetUniformLocation(myShader, "iIndex");
locColor= glGetUniformLocation(myShader, "vColorValue");
locFlag= glGetUniformLocation(myShader, "bSomeFlag");
... ...
glUseProgram(myShader);
glUniform1f(locTime, 45.2f);
glUniform1i(locIndex, 42);
glUniform4f(locColor, 1.0f, 0.0f, 0.0f, 1.0f);
glUniform1i(locFlag, GL_FALSE); //布尔值是用整数代表的,0为假,1为真

设置统一数组
glUniform能指定一个指针,例如数组。
void glUniform1fv(GLint location, GLuint count, Glfloat *v);
void glUniform2fv(GLint location, GLuint count, Glfloat *v);
void glUniform3fv(GLint location, GLuint count, Glfloat *v);
void glUniform4fv(GLint location, GLuint count, Glfloat *v);
void glUniform1iv(GLint location, GLuint count, Glint *v);
void glUniform2iv(GLint location, GLuint count, Glint *v);
void glUniform3iv(GLint location, GLuint count, Glint *v);
void glUniform4iv(GLint location, GLuint count, Glint *v);
例如:uniform vec4 vColor;
传递时用GLfloat vColor[4] = { 1.0f , 1.0f , 1.0f, 1.0f } 定义数组,glUniform4fv(iColorLocation, 1, vColor); 代表传递过去1个一维数组(代表4分量vec4的一维数组)
其中glUniform4fv函数名的'4'意义就是4分量,第二个参数是代表传递多少个4分量,如果要传递2个4分量时,如下例子定义数组:
GLfloat vColors[2][4] = { {1.0f,1.0f,1.0f,1.0f},  {1.0f, 0.0f, 0.0f, 1.0f } };  //可见是一个二维数组[2][4], 第二维度只有4个分量。
glUniform4fv(iColorLocation, 2, vColors);

设置统一矩阵

glUniformMatrix2fv(Glint location, GLuint count, GLboolean transpose, const GLfloat *m); // 2*2矩阵
glUniformMatrix3fv(Glint location, GLuint count, GLboolean transpose, const GLfloat *m); // 3*3
glUniformMatrix4fv(Glint location, GLuint count, GLboolean transpose, const GLfloat *m); // 4*4
count: 代表指针参数n的存储的矩阵数量,m是一个矩阵数组指针
transpose: 设置的矩阵若按列优先顺序存储,则应设置为GL_TRUE,否则设置为GL_FALSE,这样就会对它进行变换(即转回列优先顺序存储)
例如:在Direct3D需要将列优先顺序存储的矩阵变换成行优先排序,若原矩阵是按列优先顺序存储的,可以将transpose设置为GL_FALSE 由它进行变换为行优先顺序存储

平面着色器

示例着色器功能将几何图形进行变换并将其设置为一个单色。
属性:顶点位置
需两个统一值一个变换矩阵、一个颜色值

示例文章:https://blog.csdn.net/qq_39574690/article/details/115387925

主要代码:
顶点着色器代码: 定义一个uniform矩阵代表模型视图投影矩阵用于变换顶点位置属性,输入值也只有顶点位置属性。

// Flat Shader
// Vertex Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

// Transformation Matrix
uniform mat4	mvpMatrix;

// Incoming per vertex
in vec4 vVertex;

void main(void) 
    { 
    // This is pretty much it, transform the geometry
    gl_Position = mvpMatrix * vVertex;
    }

片段着色器代码:定义一个uniform颜色值,直接作为片段颜色输出。

// Flat Shader
// Fragment Shader
// Richard S. Wright Jr.
// OpenGL SuperBible
#version 130

// Make geometry solid
uniform vec4 vColorValue;

// Output fragment color
out vec4 vFragColor;


void main(void)
   { 
   vFragColor = vColorValue;
   }

内建函数

三角函数
 

函     数 描      述
anyFloat radians(anyFloat degress) 将角度值转化为弧度制
anyFloat degrees(anyFloat radians) 将弧度制转为角度值
anyFloat sin(anyFloat angle) 三角正弦
anyFloat cos(anyFloat angle) 三角余弦
anyFloat tan(anyFloat angle) 三角正切
anyFloat asin(anyFloat x) 反正弦
anyFloat acos(anyFloat x) 反余弦
anyFloat atan(anyFloat y, anyFloat x) y/x的反正切
anyFloat atan(anyFloat y_over_x) y_over_x的反正切
anyFloat sinh(anyFloat x) 双曲正弦
anyFloat cosh(anyFloat x) 双曲余弦
anyFloat tanh(anyFloat x) 双曲正切
anyFloat asinh(anyFloat x) 反双曲正弦
anyFloat acosh(anyFloat x) 反双曲余弦
anyFloat atanh(anyFloat x) 反双曲正切

指数函数

函  数 描  述
anyFloat pow(anyFloat x, anyFloat y) x的y次方
anyFloat exp(anyFloat x) x的自然指数
anyFloat log(anyFloat x) x的自然对数
anyFloat exp2(anyFloat x) 2的x次方
anyFloat log2(anyFloat angle) 以2为底的x的自然对数
anyFloat sqrt(anyFloat x) x的平方根
anyFloat inversesqrt(anyFloat x) x的逆平方根

几何函数

函   数 描   述
float length(vec2/vec3/vec4 x) 返回x向量的长度
float distance(vec p0, vec p1) 返回p0和p1之间的距离
float dot(vec x, vec y) 返回x和y的点乘结果
vec3 cross(vec3 x,vec3 y) 返回x和y的叉乘结果
vec normalize(vec x) 返回和x方向相同的单位长度向量
vec faceforward(vec N, vec I, vec nRef) 如果dot(Nref, I)<0 则返回N,否则返回N
vec reflect(vec I, vec N) 返回入射向量I的反射方向和表面方向N
vec refract(vec I, vec N, float eta) 返回入射向量I的反射方向、表面方向N和折射指数比eta

矩阵函数

函   数 描   述
mat matrixCompMult(mat x, mat y) 逐个分量地将两个矩阵相乘。这与线性代数的矩阵乘法不同
mat2 outerProduct(vec2 c, vec2 r) 返回一个矩阵,这个矩阵是指定的2个向量的外积(叉乘积)
mat3 outerProduct(vec3 c, vec3 r)
mat4 outerProduct(vec4 c, vec4 r)
mat2x3 outerProduct(vec3 c vec2 r)
mat3x2 outerProduct(vec2 c vec3 r)
mat2x4 outerProduct(vec4 c vec2 r)
mat4x2 outerProduct(vec2 c vec4 r)
mat3x4 outerProduct(vec4 c vec3 r)
mat4x3 outerProduct(vec3 c vec4 r)
mat2 transpose(mat2 m ) 返回一个矩阵,这个矩阵是指定矩阵的转置矩阵
mat3 transpose(mat3 m )
mat4 transpose(mat4 m )
mat2x3 transpose(mat3x2 m )
mat3x2 transpose(mat2x3 m )
mat2x4 transpose(mat4x2 m )
mat4x2 transpose(mat2x4 m )
mat3x4 transpose(mat4x3 m )
mat4x3 transpose(mat3x4 m )
float determinant(mat2 m) 返回一个矩阵,这个矩阵是指定矩阵的行列式(翻译可能有误)
float determinant(mat3 m)
float determinant(mat4 m)
mat2 inverse(mat2 m) 返回一个矩阵,这个矩阵是指定矩阵的逆矩阵
mat3 inverse(mat3 m)
mat4 inverse(mat4 m)

向量相关函数

函   数 描   述
bvec lessThan(vec x, vec y) 逐个分量地返回x<y的结果
bvec lessThan(ivec x, ivec y)
bvec lessThan(uvec x, uvec y)
bvec lessThanEqual(vec x, vec y) 逐个分量地返回x<=y的结果
bvec lessThanEqual(ivec x, ivec y)
bvec lessThanEqual(uvec x, uvec y)
bvec greaterThan(vec x, vec y) 逐个分量地返回x>y的结果
bvec greaterThan(ivec x, ivec y)
bvec greaterThan(uvec x, uvec y)
bvec greaterThanEqual(vec x, vec y) 逐个分量地返回x>=y的结果
bvec greaterThanEqual(ivec x, ivec y)
bvec greaterThanEqual(uvec x, uvec y)
bvec equal(vec x, vec y) 逐个分量地返回x==y的结果
bvec equal(ivec x, ivec y)
bvec equal(uvec x, uvec y)
bvec equal(bvec x, bvec y)
bvec notEqual(vec x, vec y) 逐个分量地返回x!=y的结果
bvec notEqual(ivec x, ivec y)
bvec notEqual(uvec x, uvec y)
bvec notEqual(bvec x, bvec y)
bool any(bvec x) 如何x的任意分量为真,则返回真
bool all(bvec x) 如果x的所有分量为真,则返回真
bvec not(bvec x) 返回x的逐个分量的补集

常用函数

函    数 描     述
anyFloat abs(anyFloat x) 返回x的绝对值
anyInt abs(anyInt x)  
anyFloat sign(anyFloat x) 返回1.0 或 -1.0 取决于x
anyInt sign(anyInt x)  
anyFloat floor(anyFloat x) 返回不大于x的最小整数
anyFloat trunc(anyFloat x) 返回不大于x的最接近的整数
anyFloat round(anyFloat x) 返回最接近x的整数值。如果是小数部分为0.5则可能取任何一个方向的整数(根据具体实现而定)
anyFloat roundEven(anyFloat x) 返回最接近x的整数的值。如果是小数部分为0.5则取最接近的偶数
anyFloat ceil(anyFloat x) 返回大于x的最接近它的整数值
anyFloat fract(anyFloat x) 返回x的小数部分
anyFloat mod(anyFloat x, anyFloat y) 返回x对于y取余得到的模数
anyFloat modf(anyFloat x, out anyFloat I) 返回x的小数部分,并将I设为余下的整数部分的值
anyFloat min(anyFloat x, anyFloat y) 返回x和y中较小的一个
anyFloat min(anyFloat x, float y)
anyInt min(anyInt x, anyInt y)
anyInt min(anyInt x, int y)
anyUInt min(anyUInt x, anyUInt y)
anyUInt min(anyUInt x, uint y)
anyFloat max(anyFloat x, anyFloat y) 返回x和y中较大的一个
anyFloat max(anyFloat x, float y)
anyInt max(anyInt x, anyInt y)
anyInt max(anyInt x, int y)
anyUInt max(anyUInt x, anyUInt y)
anyUInt max(anyUInt x, uint y)
anyFloat clamp(anyFloat x, anyFloat minVal, anyFloat maxVal) 返回缩放到minVal到maxVal范围内的x
anyFloat clamp(anyFloat x, float minVal, float maxVal)
anyInt clamp(anyInt x, anyInt minVal, anyInt maxVal)
anyInt clamp(anyInt x, int minVal, int maxVal)
anyUint clamp(anyUint x, anyUint minVal, anyUint maxVal)
anyUint clamp(anyUint x, uint minVal, uint maxVal)
anyFloat mix(anyFloat x, anyFloat y, anyFloat a) 返回x和y的线性混合,a从0到1变化
anyFloat mix(anyFloat x, anyFloat y, float a)
anyFloat mix(anyFloat x, anyFloat y, anyBool a) 在a为假时返回x的各个分量,而在a为真时返回y的各个分量
anyFloat step(anyFloat edge, anyFloat x) 如果x小于edge则返回0.0 或1.0 ,否则返回1.0
anyFloat step(float edge, anyFloat x)
anyFloat smoothstep(anyFloat edge0, anyFloat edge1, anyFloat x) 如果x<=edge0则返回0.0,如果x>=edge1则返回1.0,如果在两者之间则在0.0和1.0之间取一个平滑的Hermite插值
anyFloat smoothstep(float edge0, float edge1, anyFloat x)
anyBool isnan(anyFloat x) Return true ifxis Nan
anyBool isinf(anyFloat x) 如果x为正无穷大或负无穷大,则返回真
anyInt floatBitsToInt(anyFloat x) 将一个浮点值转换成整数值
anyUint floatBitsToUint(anyFloat x)
anyFloat intBitsToFloat(anyInt x) 将一个整数值转换成浮点值
anyFloat uintBitsToFloat(anyUint x)

模拟光线Simulating Light

模拟光线是计算机图形学的基本技术之一。以下内容介绍基本的照明知识,用GLSL实现它们。

简单漫射光

漫射光是普遍使用的,是一种经过平面反射的定向光,其强度与光线在表面上的入射角成正比。例如:光线直接射向表面的话,物体表面会比光线以一个很大的角度倾斜着射向表面的时候亮度高。实际这就是很多通过照亮物体表面产生阴影的光照模型的漫射光分量。

漫反射需要两个向量,表面法线和光源的向量,将它们先单位化再求点积,得到漫射光分量(即入射光向量和法线夹角的余弦值)应用到颜色上。

公式:float intensity = dot(vSurfaceNormal, vLightDirection);

点光源漫反射着色器

https://blog.csdn.net/qq_39574690/article/details/115439756

ADS光照模型

Phone着色

访问纹理

只有纹理单元

照亮纹理单元

丢弃片段

卡通着色(Cell Shading)——将纹理单元作为光线

猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/115314428