In this section, you’ll learn about OpenGL’s shader language, GLSL.

This section effect

www.bilibili.com/video/BV1jt…

The main content

  1. What is a shader
  2. How do I use Shader in OpenGL
  3. How to write vertex shaders and fragment shaders
  4. Code implementation process

What is a shader

As mentioned, 3D programming does two things.

  1. Where is the object displayed?
  2. What does the object look like?

A shader, also known as a shader, literally means for coloring. Of course, it also completes the transformation of objects in space. In other words, 3D rendering pipeline is implemented in the shader, of course, it needs to cooperate with OpenGL related rendering instructions to complete more advanced operations, such as: frame buffer sampling, pixel buffer application, etc.

It is a small piece of C-like code, strongly typed, that needs to be compiled into gPU-executable instructions.

Each stage of the 3D rendering pipeline corresponds to a different shader, which performs different functions. The most common shaders are vertex shaders and fragment shaders, which can meet most of our daily needs.

How do I use shader in OpenGL

OpenGL manages the shader through an object called ShaderProrgam. All shaders need to be attached to this object before being used, and then use this object to enable the use of the shader.

So when will shader be used? Of course, when drawing, for example, before executing glDrawArrays(), we need to enable the shader, and then the graph drawn by glDrawArrays() will apply the shader. After glDrawArrays() is called, we need to disable the current shader. To avoid affecting the drawing operation of other objects. This programming pattern can be summarized as:

  1. Setting a state
  2. Perform some operations
  3. Disables the previously set state

This programming mode is often used in OpenGL programming, because OpenGL itself is based on state machines, which is also for performance reasons. OpenGL every time it executes a particular instruction, if the instruction depends on a particular state, it checks to see if that state has been set, and if it has, it uses the previous state, and if you don’t change it, then all of the subsequent instructions will use that state, otherwise it will use the default state. So, get into the habit of setting state -> Performing action -> Disabling state, so that the state you set now only affects what you do next, not the rest of the program.

The steps for using shader in OpenGL are not too complicated:

  1. Writing shader source
  2. Create the ShaderProgram object, compile the shader source, and append the Shader source to the ShaderProgram object
  3. Using the shader

Among them, the second and third steps are relatively fixed, please check the source code of shaderprogam. h/ CPP implementation, mainly is how to write shader?

How to write a Shader

Before writing, we need to understand the general structure of the shader. It is actually very simple. Here is a basic shader structure.

Vertex shaders:

Fragment shaders, structure shaders and vertex shaders are consistent.

  1. Version Declaration section

Declare the version of the shader because the keywords, built-in variables, and built-in functions differ from version to version of the shader. And the shader that OpenGL ES uses is also a slightly different shader that OpenGL for PC uses.

  1. Enter the vertex properties section

Vertex properties, as the name implies, are vertex properties. Because vertices have other properties, such as normal direction, color, texture coordinates, etc., regardless of spatial position. For example, for a character in a game, the spatial position of the vertex only describes the shape of the character, and the character is usually pasted with textures, so each vertex needs to store its corresponding texture coordinates, through which OpenGL can correctly sample the color of the texture image, and then display the character pasted with the map. The shader above has two vertex input properties, vertex position and vertex color. We didn’t use textures here, but we’ll leave that for the next section.

  1. Uniform variable

Some books translate it as a consistent variable, which is very uncomfortable to read. Uniform means that the variable applies to every vertex. It is important to note that every vertex goes through the 3D rendering pipeline, which means that every vertex goes through vertex shaders, fragment shaders, geometry shaders, calculation shaders, etc. Of course, vertex shaders and fragment shaders are required. Uniform variables are variables that have the same effect on each vertex. For example, each vertex can be displayed on the screen only after world transformation, camera transformation, and projection transformation. Then these transformation matrices can be declared as uniform variables. If you have other requirements, you can certainly add your own uniform variable.

  1. The out variable

Indicates that this variable is output to the next stage of the shader. The vertex shader outputs the vertex color to the next shader, the fragment shader. Someone said, did you just output to the next fragment shader without doing anything? Why don’t you just put an in color variable in the fragment shader?

Because an assembly line is an assembly line because it’s ordered. The first stage we go through to start 3D rendering is vertex shader, then fragment shader… All of our in variables must be set in the vertex shader and then passed to the other shaders. Unless, of course, you declare it as a UNIFORM variable, which can be set at any stage in the 3D rendering pipeline.

  1. The main function

Each shader has a main function with a syntax similar to that of THE C language.

The function of the vertex shader and fragment shader above is to transform the vertex position and color according to the given transformation matrix (described in the previous tutorial) to the standardized device coordinate system and assign a value to gl_Position, which is a built-in variable of GLSL.

The vertex colors are then passed to the fragment shader, which does nothing but output the vertex colors to the next stage for processing.

Code implementation process

  1. First, we create a new ShaderProgram class that encapsulates the shader process: compile, link, enable, disable, and so on.
  1. Then we can customize our own shader based on our actual needs. For example, the shader you use to draw terrain is definitely different from the shader you use to draw the sun. The shader for the sun should be “glowing and dazzling”, while the shader for your terrain should be mainly textured, fog effect, etc. Each display implementation needs to define its own shader. In the code implementation, we give two shaders, one to render a colored triangle basicShader, and one to render a wire-framed EarthShader. When we talk about textures, let’s render a textured earth. Now, the two shaders are very simple, so the basic framework code is similar, and we will continue to enrich the EarthShader.
  1. We then defined an EarthRenderer to render the earth. It is called in the MasterRenderer so that we can separate the code that renders a particular object from the MasterRenderer for easy maintenance.
  1. Then you can start rendering.

First of all, we need to prepare the data for rendering. Here we generate the code. Of course, we can externally load the model built in 3D modeling software.

Then we can render in a frame loop. For demonstration purposes, the current function interface is poorly written and will be optimized as needed.

  1. At this point, you should be able to see what happened at the beginning of this article.

This section source: https://ww.lanzous.com/icfc4ud

Ok, end of this section.

In the next section, we will draw a textured earth.