Vulkan

I. The Basics---Chapter 1----Making Shaders

We glossed over exactly how these text strings called shaders actually get sent to OpenGL. We will go into some detail on that now.

Note

If you are familiar with how shaders work in other APIs like Direct3D, that will not help you here. OpenGL shaders work very differently from the way they work in other APIs.OpenGL shaders与其他的API工作原理有很大的不同.

Shaders are written in a C-like language. So OpenGL uses a very C-like compilation model. In C, each individual .c file is compiled into an object file. Then, one or more object files are linked together into a single program (or static/shared library). OpenGL does something very similar.

A shader string is compiled into a shader object; this is analogous to an object file. One or more shader objects is linked into a program object.

A program object in OpenGL contains code for all of the shaders to be used for rendering. In the tutorial, we have a vertex and a fragment shader; both of these are linked together into a single program object. Building that program object is the responsibility of this code:

Example 1.6. Program Initialization

void InitializeProgram()
{
    std::vector<GLuint> shaderList;
    
    shaderList.push_back(CreateShader(GL_VERTEX_SHADER, strVertexShader));
    shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader));
    
    theProgram = CreateProgram(shaderList);

    std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader);
}

The first statement simply creates a list of the shader objects we intend to link together. The next two statements compile our two shader strings. The CreateShader function is a function defined by the tutorial that compiles a shader.

第一行代码创建一个我们要link在一起的shader object的表.接下来两行编译shader. CreateShader()是我们自定义的函数来编译shader,该函数的实现在下面.

Compiling a shader into a shader object is a lot like compiling source code. Most important of all, it involves error checking. This is the implementation of CreateShader:

Example 1.7. Shader Creation

GLuint CreateShader(GLenum eShaderType, const std::string &strShaderFile)
{
    GLuint shader = glCreateShader(eShaderType);
    const char *strFileData = strShaderFile.c_str();
    glShaderSource(shader, 1, &strFileData, NULL);
    
    glCompileShader(shader);
    
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE)
    {
        GLint infoLogLength;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
        
        GLchar *strInfoLog = new GLchar[infoLogLength + 1];
        glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog);
        
        const char *strShaderType = NULL;
        switch(eShaderType)
        {
        case GL_VERTEX_SHADER: strShaderType = "vertex"; break;
        case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break;
        case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break;
        }
        
        fprintf(stderr, "Compile failure in %s shader:\n%s\n", strShaderType, strInfoLog);
        delete[] strInfoLog;
    }

	return shader;
}

An OpenGL shader object is, as the name suggests, an object. So the first step is to create the object with glCreateShader. This function creates a shader of a particular type (vertex or fragment), so it takes a parameter that tells what kind of object it creates. Since each shader stage has certain syntax rules and pre-defined variables and constants (thus making different shader stages different dialects of GLSL), the compiler must be told what shader stage is being compiled.

一个OpenGL shader object,像名称说明的那样,是一个object.所以第一步是使用glCreateShader()创建一个object.这个函数创建一个特定类型的shader(vertex或fragment),它有意个参数来标识创建何种shader.因为每个shader阶段有确定的语法规则和 预先定义的变量和常量,必须通知编译器编译的是哪个shader阶段.

Note

Shader and program objects are objects in OpenGL. But they work rather differently from other kinds of OpenGL objects. For example, creating buffer objects, as shown above, uses a function of the form glGen* where * is Buffer. It takes a number of objects to create and a list to put those object handles in.

There are many other differences between shader/program objects and other kinds of OpenGL objects.

The next step is to actually compile the text shader into the object. The C-style string is retrieved from the C++ std::string object, and it is fed into the shader object with the glShaderSource function. The first parameter is the shader object to put the string into. The next parameter is the number of strings to put into the shader. Compiling multiple strings into a single shader object works analogously to compiling header files in C files. Except of course that the .c file explicitly lists the files it includes, while you must manually add them withglShaderSource.

void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lenOfStrings);

下一步是编译text shader为object.C风格string接收string object,使用glShaderSource()将它填入shader object.第一个参数代表shader容器的句柄(由glCreateShader返回的整形数).第二个参数是存入装入shader的字符串的个数.编译多个字符串文件为一个shader object原理与编译C文件的头文件类似.C文件显式的列出了它包含的文件,所以你必须手动的使用glShaderSource将你要编译的string添加他们.

The next parameter is an array of const char* strings. The last parameter is normally an array of lengths of the strings. We pass in NULL, which tells OpenGL to assume that the string is null-terminated. In general, unless you need to use the null character in a string, there is no need to use the last parameter.

第四个参数strings是包含源程序的字符串数组;lenOfStrings是一个数组,数组中的元素代表了strings相应下标的字符串的长度,如果这个值被设置成NULL,则代表每个字符串是以NULL结尾的.除非你需要在字符串中使用NULL,否则没必要使用最后一个参数.

Once the strings are in the object, they are compiled with glCompileShader, which does exactly what it says.

After compiling, we need to see if the compilation was successful. We do this by calling glGetShaderiv to retrieve theGL_COMPILE_STATUS. If this is GL_FALSE, then the shader failed to compile; otherwise compiling was successful.

If compilation fails, we do some error reporting. It prints a message to stderr that explains what failed to compile. It also prints an info log from OpenGL that describes the error; think of this log as the compiler output from a regular C compilation.

After creating both shader objects, we then pass them on to the CreateProgram function:

Example 1.8. Program Creation

GLuint CreateProgram(const std::vector<GLuint> &shaderList)
{
    GLuint program = glCreateProgram();
    
    for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
    	glAttachShader(program, shaderList[iLoop]);
    
    glLinkProgram(program);
    
    GLint status;
    glGetProgramiv (program, GL_LINK_STATUS, &status);
    if (status == GL_FALSE)
    {
        GLint infoLogLength;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
        
        GLchar *strInfoLog = new GLchar[infoLogLength + 1];
        glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog);
        fprintf(stderr, "Linker failure: %s\n", strInfoLog);
        delete[] strInfoLog;
    }
    
    for(size_t iLoop = 0; iLoop < shaderList.size(); iLoop++)
        glDetachShader(program, shaderList[iLoop]);

    return program;
}

This function is fairly simple. It first creates an empty program object with glCreateProgram. This function takes no parameters; remember that program objects are a combination of all shader stages.

函数很简单.第一行使用glCreateProgram()创建一个空的program object.这个函数没有参数,它的目的是联合所有的shader阶段.

Next, it attaches each of the previously created shader objects to the programs, by calling the function glAttachShader in a loop over thestd::vector of shader objects. The program does not need to be told what stage each shader object is for; the shader object itself remembers this.

接下来,它将前面创建的所有shader object附加到program,使用glAttachShader ()和一个循环.不需要告诉program每个shader object的类型是什么,shader object自己记录下来.

Once all of the shader objects are attached, the code links the program with glLinkProgram. Similar to before, we must then fetch the linking status by calling glGetProgramiv with GL_LINK_STATUS. If it is GL_FALSE, then the linking failed and we print the linking log. Otherwise, we return the created program.

当所有shader object被attach起来以后,使用函数glLinkProgram()来link program.像前面一样我们需要使用函数获得link的状态.如果是GL_FALSE,说明link失败,需要打印link日志.

Note

In the above shaders, the attribute index for the vertex shader input position was assigned directly in the shader itself. There are other ways to assign attribute indices to attributes besides layout(location = #). OpenGL will even assign an attribute index if you do not use any of them. Therefore, it is possible that you may not know the attribute index of an attribute. If you need to query the attribute index, you may call glGetAttribLocation with the program object and a string containing the attribute's name.

Once the program was successfully linked, the shader objects are removed from the program with glDetachShader. The program's linking status and functionality is not affected by the removal of the shaders. All it does is tell OpenGL that these objects are no longer associated with the program.

一旦program被成功的link,我们使用glDetachShader将shader object从program移除.program的link状态和功能不会被移除shader影响.所有谢谢告诉OpenGL:program不会再与那些shader有联系.

After the program has successfully linked, and the shader objects removed from the program, the shader objects are deleted using the C++ algorithm std::for_each. This line loops over each of the shaders in the list and calls glDeleteShader on them.

Using Programs. To tell OpenGL that rendering commands should use a particular program object, the glUseProgram function is called. In the tutorial this is called twice in the display function. It is called with the global theProgram, which tells OpenGL that we want to use that program for rendering until further notice. It is later called with 0, which tells OpenGL that no programs will be used for rendering.

Note

For the purposes of these tutorials, using program objects is not optional. OpenGL does have, in its compatibility profile, default rendering state that takes over when a program is not being used. We will not be using this, and you are encouraged to avoid its use as well.

posted on 2012-11-16 19:42  Vulkan  阅读(121)  评论(0编辑  收藏  举报

导航