OpenGL着色器语言

初识OpenGL着色器语言


未完待续:


1、着色器架构

OpenGL着色器语言(GLSL)由OpenGL实现链接和编译,完全在图形硬件中运行。

OpenGL着色器语言_第1张图片

至少需要两个着色器

  • 顶点着色器
  • 片段着色器。
  • 还有一种可选的着色器称为几何着色器。

我们可以在3中方式中选择一种向顶点着色器传递数据:

  1. 参数,是对每个顶点而言的。
  2. 统一值,是针对整个顶点数据批次的常量。
  3. 加载和使用纹理数据。

可以为片段着色器设置统一值和纹理数据。

注意:将顶点属性发送到片段着色器毫无意义,因为片段着色器只是用来在土元进行光栅化后对片段进行填充(最基本的是像素)。

着色器程序的从入口点main函数开始。


2、变量和数据类型

数据类型:

  • 整数(有符号和无符号)
  • 浮点数(单精度)
  • 布尔值

    没有指针,且没意任何类型的字符串和字符。但函数可以还回这些类型的一种,也可以声明为void。

向量类型:

向量数据类型 描述
vec2,vec3,vec4 2分量,3分量,4分量浮点向量
ivec2,ivec3,ivec4 2分量,3分量,4分量整数向量
uvec2,uvec3,uvec4 2分量,3分量,4分量无符号整数向量
bvec2,bvec3,bvec4 2分量,3分量,4分量布尔向量

我们可以使用一个“构造函数初始化一个向量。
例如:

vec4 vVertexPos = vec4(39.0f, 10.0f, 0.0f, 1.0f);

有关向量的操作:

vVertexPos = vOldPos + vOffset;  //两个向量相加
vVertexPos = vNewPos;   //赋值
vVertexPos = vec4(1.0f, 1.0f, 0.1f, 0.0f); 
vVertexPos *= 5.0f;   //进行缩放

可以对独立元素进行寻址:
1.典型情况下:

vVertexPos.x = 3.0f;
vVertexPos.xy = vec2(3.0f, 5.0f);
vVertexPos.xyz = vNewPos.xyz;

2.进行颜色操作时使用rgba:

vOutputColor.r = 1.0f;

3.进行纹理操作时用stpq。


4.向量类型还支持调换操作:
如:

vNewColor.bgra = vOtherVertex.x + 5.0f;

向量类型不只是着色器的本地数据类型,也是硬件的数据类型,速度很快。

矩阵类型:
所以的矩阵只支持浮点数。

矩阵类型 描述
mat2,mat2x2 两行两列
mat3,mat3x3 三行三列
mat4,mat4x4 四行四列
mat2x3 两行三列
mat2x4 两行四列
mat3x2 三行两列
mat3x4 三行四列
mat4x2 四行两列
mat4x3 四行三列

一个矩阵就是一个向量组成的数组—-列向量
矩阵的相关操作:

mModelView[3] = vec4(0.0f, 0.0f, 0.0f, 1.0f);   /*设置矩阵的最后一列*/

vec4 vTranslation = mModelView[3];  /*恢复一个矩阵的最后一列*/
vec4 vTranslation = mModelView[3].xyz;      /**/

vec4 vVertex;
mat4 mvpMatrix;
vOutPos = mvpMatrix * vVertex;  //矩阵乘以向量

mat4 vTransform = mat4(1.0f);  //构造一个单位矩阵

3、存储限定符

——限定符用于将变量标记为输入变量(in或uniform)、输出变量(out)或常量。

  • 输入变量:接受来自OpenGL客户端(通过C/C++提交的属性)或者以前的着色器阶段(例如从顶点着色器传递到片段着色器)。
  • 输出变量:指在任何着色器阶段进行写入的变量,我们希望在后续的着色器阶段能看到这些变量。如,从顶点着色器传递到片段着色器。
限定符 描述
const 一个编译时的常量
in 一个从以前的阶段传递过来的变量
in centroid 一个从以前的阶段传递过来的变量,使用质心插值
out 传递到下一个处理阶段或者在一个函数中指定一个还回值
out centroid 传递到下一个处理阶段,使用质心插值
uniform 一个从客户端传递过来的变量,在顶点之间不做改变
inout 一个读/写变量,只能用于局部函数参数
< none> 只是普通的本地变量,外部不可见,外部不可访问

补充:
1. inout只能在一个函数中声明一个参数时使用。这是将一个值传递 到一个函数并且允许这个函数修改并还回同一变量值得唯一方法。
2. 在但采样缓存区中,插值操作总是从像素中心开始的,限定符controid只对一个多重采样缓存区起作用。
3. 默认情况下,参数将在两个着色器阶段之间以一种透视正确的方法进行插补。noperspective关键字来指定一个非透视插值,flat关键词不进行插值,smooth是以一种透视正确的方法进行插补。


4、真正的着色器

GLShanderManager类有个单位存储着色器。—–这种着色器不会对集合图像进行转换,而是使用单一的颜色来绘制图元。

ShadedIdentity顶点程序:

#version 330     //指定着色器要求的OpneGL着色语言的最低版本为3.3

//属性声明
in vec4 vVertex;   //顶点位置属性
in vec4 vColor;     //顶点颜色属性
out vec4 vVaryingColor;     /*传递到片段着色器的颜色值,这个变量将成为要传递到片段着色器的顶点的颜色值,逐个着变量必须为片段着色器声明一个in变量*/

void main(void)
{   
    vVaryingColor = vColor;    // 简单复制颜色值
    gl_Postion = vVertex;      //简单传递顶点位置
}

ShadedIdentity着色器片段程序:

#version 330
out vec4 vFragColor;    //将要进行光栅化的片段颜色
in vec4 vFrayingColor;  //从顶点阶段得到的颜色
void main(void)
{
    vFragColor = vVaryingColor;  //对片段进行颜色插值
}
  • 属性声明:是由C/C++客户端OpenGL代码逐个顶点进行指定的,每个顶点程序中最多可以有16个属性。另外,标记为in的变量是只读的。
  • 顶点动作:主函数main中的内容,gl_Position是一个预定义的内建4个分量向量,它包含顶点着色器要求的一个输出。输入gl_Position的值是集合图形阶段用来装配图元的。
  • 片段三角形:在渲染一个图元时,一旦3个顶点有顶点程序进行处理,那么他们将组成一个三角形,这个三角形将由硬件进行光栅化。如果片段程序只有一个输出,那么它在内部将被分配为“输出0”。这个片段着色器的一个输出,并将传递到有glDrawBuffers设置的缓存区目标。

5、编译、绑定和链接

顶点着色器的后缀名.vp。
片段着色器的后缀名.fp。

gltLoadShaderPairWithAttributes函数

/////////////////////////////////////////////////
//加载一对着色器,进行编译并链接到一起
//为每个着色器指定完整的源文本
//在着色器名之后,指定参数数量,然后指定索引和每个参数的参数名

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);
        cout << "The shader at "<< szVertexProg
             << "could not be found.\n";
        return (GLuint)NULL;
    }
    //片段程序
        if(gltLoadShaderFile(szFragmentProg, hVFragmentShader) == false)
    {
        glDeleteShader(hVertexShader);
        glDeleteShader(hFragmentShader);
        cout << "The shader at "<< szFragmentProg
             << "could not be found.\n";
        return (GLuint)NULL;
    }
    //对两者进行编译
    glCompileShader(hVertexShader);
    glCompileShader(hFragmentShader);
    //在顶点着色器检查错误
    glGetShaderiv(hVertexShader, GL_COMPILE_SHADER,    
                                &testVal);
    if(testVal == GL_FLASE)
    {
        char infoLog[1024];
        glGetShaderInfoLog(hVertexShader, 1024, NULL, 
                            infoLog);
        cout<<"The shader at"<" failed to compile with the following error:\n"<< infoLog << "\n";
        glDeleteShader(hVertexShader);
        glDeleteShader(hFragmentShader);
        return (GLuint)NULL;
    }
        //在片段着色器检查错误
    glGetShaderiv(hFragmentShader, GL_COMPILE_SHADER,    
                                &testVal);
    if(testVal == GL_FLASE)
    {
        char infoLog[1024];
        glGetShaderInfoLog(hFragmentShader, 1024, NULL, 
                            infoLog);
        cout<<"The shader at"<" failed to compile with the following error:\n"<< infoLog << "\n";
        glDeleteShader(hVertexShader);
        glDeleteShader(hFragmentShader);
        return (GLuint)NULL;
    }

    //创建最终的程序对象,并链接着色器
    hReturn = glCreateProgram();
    glAttrachShader(hReturn,hVertexShader);
    glAttrachShader(hReturn,hFragmentShader);
    //现在需要将参数名绑定到他们指定的参数位置列表上
    va_list attributeList;
    va_start(attributeList, szFragmentShader);
    //重复迭代这个参数列表;
    char *szNextArg;
    int iArgCount = va_arg(attributeList, int); //参数数量
    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_FLASE)
    {
        char infoLog[1024];
        glGetProgramInfoLog(hReturn, 1024, NULL, infoLog);
        cout<<"The program "<" failed to link with the following error:\n"<< infoLog<< "\n";
        glDeleteProgram(hReturn);
        return (GLuint)NULL;
    }
    return hReturn;
}

加载一个着色器所以必须元素:

  • 指定属性:例如,

    hShader = gltLoadShaderPairWithAttributes("vertexProg.vp", fragmentProg.fp, 2, 0, "vVertexPos", 1, "vNormal");

    用来绑定一个带有顶点位置和表面法线属性的着色器。
  • GLTools类GLBatch和GLTriangleBatch使用一系列一直的属性位置,

    typedef enum GLT_SHADER_ATTRIBUTE{GLUT_ATTRIBUTE_VERTEX = 0,
    GLUT_ATTRIBUTE_COLOR, GLUT_ATTRIBUTE_NORNAL,
    GLUT_ATTRIBUTE_TEXTURE0, GLUT_ATTRIBUTE_TEXTURE1,
    GLUT_ATTRIBUTE_TEXTURE2, GLUT_ATTRIBUTE_TEXTURE3,
    GLUT_ATTRIBUTE_LAST };

    使用这些属性位置标识符,我们就可以开始和GLShaderManager类中支持的存储着色器一起使用自己的着色器了。
  • 设置源代码:
    首要任务是创建两个着色器对象,分别对应顶点着色器和片段着色器。
hVertexShader = glCreateShader(GL_VERTEX_SHADER);
hFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

然后,可以使用者两个着色器ID来加载着色器源代码。


GLchar *fsStringPtr[1];
fsStringPtr[0] = (GLchar *)szShaderSrc;
glShaderSource(shader, 1, (const GLchar **)fsStringPtr, NULL);

  • 编译着色器
  • 进行连接和绑定
  • 连接着色器

6、使用着色器

glUseProgram(myShaderProgram);将着色器设置成活动的,这样在顶点和片段着色器会处理所以提交的几何图形。在提交顶点属性之前,要对Uniform值和纹理进行设置。


着色器统一值

1、寻址统一值

在着色器编译和链接后,我们必须在着色器中寻找统一值位置。

GLint glGetUniformLocation(GLuint shaderID, const GLchar * varName);

代表在shaderID指定的着色器中由valName命名的变量的位置。


void glUniform1f(GLint location, GLfloat v0);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
    .
    .
    .
void glUniform4i(GLint Location, GLint v0, GLint v1, GLint v2, GLint v3);

例如:

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);

你可能感兴趣的:(OpneGL)