OpenGL tutorial (4) shader (1)

Source: Internet
Author: User

Original post address: http://ogldev.atspace.co.uk/www/tutorial04/tutorial04.html

This chapter begins to learn how to use shader. In the past, many people often used OpenGL fixed pipelines to do some programs. shader is rarely used, while modern GPU programming and shader applications are indispensable. Although shader programming is used, A little more code, but more flexible.

The shader pipeline diagram of OpenGL is as follows. Note that tessellation is missing, while tessellation in OpenGL is the same as that in d3d11, but the names of each stage are different, it will be mentioned later in the tutorial.

The first step is vertex processor (VS), the second step is geometry processor (GS), and the third step is the clip operation (which should also include Pa, primitive assembly ), finally, raster and Fragment Processor (PS ).

Let's take a look at the introduction of the d3d11 tutorial and compare it with the OpenGL pipeline. In fact, apart from the name, they are essentially the same, because they are all the same hardware.

Http://www.cnblogs.com/mikewolf2002/archive/2012/03/24/2415141.html

 

1. vertex processor. In this phase, the shader operation is performed on each vertex. The number of vertices is specified in the draw function. Vertex shader does not have any body element semantics. It only targets vertex operations, such as coordinate space changes and texture coordinate changes. Each vertex must execute the vertex shader and cannot skip this stage. After the vertex shader is executed, the vertex enters the next stage.

2. geometry processor (geometry processor). In this phase, vertex joining relationships and body semantics are passed into the shader. In the geometric shader, it not only processes the vertex itself, but also considers a lot of additional information. Geometric shader can even change the semantic type of the output Body element. For example, the input Body element is a series of separate points (point list Body element semantics), and the output Body element is a triangle or a quadrilateral composed of two triangles, we can even Input Multiple vertices in the ry shader to output the body elements with different semantics for each vertex.

3. the Clipper stage is also called the cropping stage. This is a fixed pipeline module. It uses six face elements of the cropping space for cropping, and the parts outside the cropping space will be removed, in this case, a new vertex and a new triangle may be generated. You can also define your own cropping plane to crop the cropped triangle. The cropped triangle will be uploaded to the grating stage. [Note: Prior to clipper, there will be a PA phase in which vertices processed by vertex shader or geometric shader will be reassembled into triangles]

4. in the raster phase and fragment operation phase, the triangle after the Clipper will be raked first to generate a lot of fragment (fragment), and then execute the fragment shader. In the fragment shader, the texture may be loaded to produce the final pixel color. Fragment can be understood as a pixel with sample and depth information]

Unlike d3d11 pipelines, OpenGL vertex shader, geometric shader, and segment element shader are optional. If they are not specified, the default function is executed.

Shader management is similar to creating a C/C ++ program. First, write the shader code, put the code in a text file or a string, and then compile the shader code, put the compiled shader code into each shader object, link the shader object to the program, and finally send the shader to the GPU.

When linking the shader, the driver may optimize the shader Code. For example, your vertex shader may output a forward parameter, but the subsequent part of the shader does not use it, the driver may optimize the operation and remove the output of this parameter to speed up the execution of the vertex shader.

 

To compile the shader program in OpenGL, follow these steps:

First, create a shader program object, and then create the corresponding shader objects, such as vertex shader objects and slice object shader objects, and connect them to the shader program object. When creating a shader object, you must load the shader source code, compile the shader, and link it to the shader object.

The following is the main code:

GLuint ShaderProgram = glCreateProgram();

First, we create a shader program object and link all shader to This shader program object.

GLuint ShaderObj = glCreateShader(ShaderType);

We use the above function to create two shader objects. One of the Shader types is gl_vertex_shader, and the other is gl_fragment_shader, the shader source code and the shader compilation function are the same for the two types of shader.

const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0]= strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);

Before compiling the shader, we first need to use the glshadersource function to specify the shader source code. This function can use the character pointer array (actually a two-dimensional pointer const glchar **, each element is a character pointer, specifies multiple shader source codes. The first parameter of this function is the shader object, and the second parameter is an integer. It specifies the number of elements in the character pointer array, that is, the number of source codes. The third parameter is the character pointer array address, the fourth parameter is an integer pointer array, which corresponds to the shader character pointer array, which specifies the number of characters in each shader source code. To make the program simple, in glshadersource, there is only one element in the character pointer array, that is, only one slot is used to store the source code.

glCompileShader(ShaderObj);

The above function is used to compile the shader object.

GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
    GLchar InfoLog[1024];
    glGetShaderInfoLog(ShaderObj, sizeof(InfoLog), NULL, InfoLog);
    fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
}

Generally, various errors may occur during shader compilation. At this time, we can get the compilation status through the above Code and print out the error information to facilitate debugging of shader code.

glAttachShader(ShaderProgram, ShaderObj);

Finally, the compiled shader object is bound to the shader program object.

glLinkProgram(ShaderProgram);

After binding, it is a link operation. After the link operation, we can release the intermediate shader object through the gldeleteshader function.

glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
if (Success == 0) {
    glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
    fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
}

Through the above code, we can check whether there is an error in the shader link. Note that the code for detecting the shader Link error is somewhat different from the code for detecting the shader compilation (the biggest difference is that different functions are used ).

glValidateProgram(ShaderProgram);

Verify the validity of the shader program object. You may be confused here: since the previous link has been successfully linked, why should we verify the validity again? In fact, there is a difference between them. The link detection is based on the shader binding, and the verification validity is to verify whether the program can be executed on the current pipeline. In complex applications, there are multiple shader and multiple States. Before each draw, it is best to perform an operation to verify the validity. In our program, only one verification is performed. Of course, in order to reduce the verification overhead, we can perform verification operations only in the debug stage, and the final release stage does not need these operations, thus reducing program overhead and improving performance.

glUseProgram(ShaderProgram);

Finally, we use the above function to send the linked program object to the shader pipeline. This shader will be valid for all subsequent draws unless you replace it with another shader program object or disable it by setting gluseprogram (null) (the fixed pipeline function will be enabled at this time ). If you only specify one shader object, such as vertex shader, fragment shader uses the fixed pipeline function.

We have learned about the shader management function. Let's take a look at the code of vertex and fragment shader: (included in the 'pvs' and 'pfs 'variables, respectively, the modified code is placed in color. vs and color. PS, and read and write the shader code file through a gclfile class, so that it is easy to debug the shader code ).

#version 400

This tells the compiler to use glsl of Version 4.0. If the compiler does not support it, an error message is generated.

layout (location = 0) in vec3 Position;

This declaration specifies that the attribute value of the vertex in the vertex shader is a three-dimensional coordinate vector, and its attribute name is position. This attribute value is valid for each vertex in the GPU that executes the vertex shader, this means that the value in the vertex buffer is interpreted as a position. Layout (location = 0) creates a binding relationship between the vertex buffer and the vertex attribute name.

Location specifies the position of this attribute in the vertex buffer, especially when the vertex contains multiple attributes (such as location, direction, texture coordinates, etc.), we must let the compiler know, the vertex attribute corresponds to the position in the vertex buffer. there are usually two ways to implement this. One is like the location we specified in the shader = 0. In this case, in the CPP source code, we specify the shader attribute through hard encoding, such as glvertexattributepointer (0). In another case, we can simply declare in vec3 position in the shader code, in the application, the glgetattriblocation function is used to find the attribute location at runtime. In this case, we need to use the returned value of glgetattriblocation instead of hard encoding. In this tutorial, we use the first method. For complex applications, we can use the second method to let the compiler decide the attribute index and then query it at runtime, this allows you to easily integrate multiple shader source files without using them to match our own buffer layout.

void main()

We can link Multiple shader objects together to form the final shader. However, for each type of shader (Vs, GS, FS), there can only be one main function in the code, this function serves as the entry point for executing the shader. For example, we can create a shader library file, which contains some functions for computing illumination, and then link it to the shader without the main function.

gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);

Here, we use hard encoding to change the vertex position. The values of X and Y are halved, and the value of Z remains unchanged. 'Gl _ position' is a built-in special variable used to store the coordinates of vertices in the same cropping space. The raster module will look for the value of this variable and use it as the value of the screen space (in fact, it is not accurate, and the viewport is changed in PA, so the vertices in vs are not the corresponding screen space ). Halving X and Y means that the rendered triangle is 1/4 of the triangle in the previous tutorial. Note: we set w = 1.0 here, which is very important. Otherwise, the rendering result may not be correct.

To project an object from 3d to 2D, two phases are required: in the first phase, all vertices are multiplied by the projection matrix, and then the GPU executes the perspective Division before raster, that is, the values of X, Y, and Z are equal to their respective values divided by the values of W, while the value of W is 1. Perspective division is completed by a fixed GPU module, usually in PA. In this tutorial, we set w = 1.0 directly, and the value after perspective Division is the same as before.

If the program runs normally, three vertices (-0.5,-0.5), (0.5,-0.5), and (0.0, 0.5) will arrive at the grating module. In this program, clipper does not need to do anything, because all of our vertices are in the normalized cropping space box. After that, the vertex values are mapped to the screen space (the operation of the view changes ), in addition, the grating module performs the grating operation on the triangle object element. After the grating operation, each object element then performs the shader operation on the object element.

The following shader is the code of the multipart shader:

out vec4 FragColor;

The function of the slice shader is to output the final color value of the slice. In the slice shader, You can discard some slice elements (pixels) or change their Z values (note: this will affect the functions of hardware early Z ). The output color is finally completed by the declared out variable fragcolor. The four components of the fragcolor vector represent the R, G, B, And a values, which are finally written to framebuffer.

FragColor = vec4(1.0, 0.0, 0.0, 1.0);

In the previous tutorial, without the shader operation, the screen will be painted with the default white color. Now we specify the red color, and a red triangle will be drawn on the screen.

After the program is executed, the effect is as follows:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.