OPENGL ES 2.0 Knowledge Talk (8) —— OPENGL ES Detailed Explanation II (Incoming Drawing Information)

Review of the previous section

The previous section described how to associate a set of available shaders to the GPU through OpenGL ES. This set of shaders is placed in a program as a whole for the GPU to use. Then the GPU not only needs this set of shaders to draw pictures, but also needs to pass some necessary input parameters to this set of shaders, such as the vertex position, shape, color and other information you want to draw the picture, then in this section, you will learn how to pass OpenGL The ES API passes the information needed for these renderings to the GPU.

For more audio and video knowledge, please pay attention to the official account: the code home of the attack

information needed to draw

If you want to draw a picture, at least you need to think about the shape of the picture you want to draw in advance, such as drawing a triangle or a circle, how big the picture is, and what color the picture should be. Therefore, it is necessary to pass these information, such as the vertex position information and vertex color information of the picture, to the GPU through the API of OpenGL ES. Then this section will explain in detail the API responsible for passing in drawing information in OpenGL ES.

Detailed explanation of OpenGL ES API

void glGenBuffers(GLsizei n, GLuint * buffers);

Our purpose is to transfer the data to the GPU. To put it more professionally, it is to transfer the data in the corresponding memory of the CPU to the memory of the GPU. For example, allocate a piece of memory in the program, and write something to be passed to the GPU in this piece of memory. This piece of memory is located in the memory corresponding to the CPU. Then there are two ways to transfer, one is to directly assign the data in the CPU memory to the corresponding variable in the GPU, the other is to pack the data in the CPU, and then create a buffer object in the GPU to pack the data in the CPU The data is passed to the buffer object in the GPU, and then the buffer is accessed and used in the GPU. Although the second method will have a little more code, it will save space and time consumption. There are two reasons for the savings. First, because the data we want to pass to the GPU is divided into many types, such as vertex coordinates, vertex colors, etc., if the first method is used to transfer, then we need to perform multiple transfers. The transfer from the CPU to the GPU is time-consuming and resource-consuming, so if we pack the data in the CPU memory and transfer all the data to a buffer object in the GPU at one time, then the GPU only needs to offset the data according to the data in the buffer object. You can assign a block of the buffer object to a variable, and another block to another variable. Reason 2: Maybe a set of vertex colors will be used by the GPU multiple times when drawing multiple times. The vertex color is passed in once, so the data needs to be passed twice, and if the buffer object is used, as long as the buffer object is not deleted, it will always exist in the GPU, so the GPU can use this buffer multiple times.

When writing a program, you must pay attention to saving space and time while ensuring the function, so the second method is generally used. In the second method, a buffer object is first required. The glGenBuffers API is used to create the name of the buffer object first, and then create a buffer object through the API glBindBuffer. Let's talk about the glGenBuffers and glBindBuffer APIs later.

The first input parameter of this function means that the API will generate n buffer object names, and when n is less than 0, an INVALID_VALUE error will occur. The second input parameter is used to save the buffer object name to be created. These buffer object names are actually numbers, and if multiple buffer object names are generated at one time, they do not necessarily have to be consecutive numbers. The buffer object name is uint type, and 0 has been reserved, so it must be an integer greater than 0.

This function has no output parameters. When the creation is successful, the names of n buffer objects that have not been used before will be generated in the second parameter buffers. Then these names will be marked as used, and this mark is only valid for the glGenBuffers API, that is, when more buffer object names are generated through this API, the previously created buffer objects names will not be used. So recall, this step is actually just creating some buffer object names, but not really creating buffer objects. Only after these buffer object names are bound by glBindBuffer, will the corresponding buffer objects be created.

void glBindBuffer(GLenum target, GLuint buffer);

The previous API glGenBuffers only created some bufferobject names, and then created a buffer object in the glBindBuffer API. The first input parameter of this function means to specify the type of buffer object, as if the shader is divided into vertex shader and fragment shader. The buffer object is also divided into types. In OpenGL ES2.0, the buffer object is divided into two types: VBO vertex buffer object and IBO index buffer object. So here, the first input parameter must be GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER. Among them, GL_ARRAY_BUFFER corresponds to VBO, and GL_ELEMENT_ARRAY_BUFFER corresponds to IBO. If other parameters are passed in, an INVALID_ENUM error will be reported. The second input parameter is the buffer object name obtained by glGenBuffers just now.

This function has no output parameters. If the buffer passed in is a newly created buffer object name, and it has not been created and associated with a buffer object, then through this API, a buffer object of a specified type will be generated, and it will be related to this Buffer object names are associated together, and when specifying a buffer object name later, it is equivalent to specifying this buffer object. The newly created buffer object is a buffer whose space is 0 and whose initial state is the default value. The initial state is GL_STATIC_DRAW. Then after the creation and association are completed, this buffer object will be regarded as the VBO or IBO used by the current GPU. If the incoming buffer already has an associated buffer object, then just specify the buffer object as the VBO or IBO used by the current GPU. Then the VBO or IBO used by the GPU is no longer in use.

So recall, create some buffer object names through glGenBuffers, and then create and associate a buffer object with the buffer object name through glBindBuffer. At the same time, through this API, set the buffer object corresponding to the parameter buffer to the VBO or VBO currently used by the GPU. IBOs. Although a large number of buffer objects can be stored in the GPU, only one VBO and one IBO can be used in a context of a thread at the same time. Afterwards, operations on the buffer, such as querying the status of the buffer, etc., will directly operate the VBO or IBO, instead of using the buffer object name. Therefore, if you want to use a certain buffer object, you must first launch this buffer through the glBindBuffer API and set it as the current VBO or IBO of the GPU.

In the initial state, VBO or IBO is bound to the buffer object whose buffer object name is 0, but in fact, the buffer object whose buffer object name is 0 is not a legal buffer object, so if you operate on VBO or IBO at this time Or query, etc., it will report GL_INVALID_OPERATIO error.

A buffer object can be bound to different targets multiple times, even if it is bound repeatedly, it does not matter, because the developers of the GPU driver will try to optimize it.

After a buffer object is used by GPU, any operation on this buffer object will affect the result of its binding with GPU. For example, after a buffer object is used by the GPU, and then the buffer object is deleted, then all BOs using this buffer object in the original context will be reset, and then it is equivalent to binding with the buffer object whose buffer object name is 0. It is equivalent to returning to the state where no buffer is bound; if other contexts or other threads also use this buffer object, there will be no change when deleting it, but once the deleted buffer is used, it will It will lead to undefine result, GL Error, drawing interruption or even program termination.

When the VBO is bound to the buffer object whose buffer object name is not 0, it means that the assignment of the attribute in the shader in the GPU will be through the VBO instead of directly assigning the data in the CPU memory to the corresponding variable in the GPU up. The attribute assignment to the shader being used in the GPU through OpenGL ES is through the glVertexAttribPoint API. This API will be explained in detail later, so let's briefly talk about it now. If the attribute in the shader is assigned a value through VBO, the last parameter of this API only needs to pass an offset, otherwise, it needs to pass in an array containing real data or a pointer to real data. You can query the name of the buffer object bound to the VBO through the glGet API, the parameter is GL_ARRAY_BUFFER_BINDING, or you can query through glGetVertexAttribiv, the parameter is GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING.

When the IBO is bound to the buffer object whose buffer object name is not 0, it means that when drawing through the glDrawElement API, the last parameter of this API only needs to pass an offset, otherwise, then You need to pass in an array containing the real data or a pointer to the real data. Other functions of this API will be described in the next section. You can query the name of the buffer object bound by IBO through the glGet API, the parameter is GL_ELEMENT_ARRAY_BUFFER_BINDING.

The buffer object name and the corresponding buffer object, like shader and program, belong to a namespace, that is, they can be shared by multiple share contexts.

void glBufferData(GLenumtarget, GLsizeiptr size, const GLvoid * data, GLenum usage);

After creating the buffer object, you need to assign data to the buffer object, and the glBufferData API is to pass the data saved on the CPU side to the GPU side through OpenGL ES, and save it in the specified buffer object.

The first input parameter of this function means to specify the type of buffer object, which can be GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER. As I have just said, after bindbuffer, the operation on buffer object will be directly operated through target instead of through The buffer object name is changed (unless the buffer object is deleted), since a context can only have one VBO and one IBO, so here we specify whether we operate VBO or IBO through target, and we can precisely specify which one is actually operated buffer object. If other parameters are passed in, an INVALID_ENUM error will be reported. The second and third input parameters mean: data is a piece of memory in the CPU that points to the actual data stored, and size is the size of data in machine units, and size cannot be negative, otherwise an INVALID_VALUE error will be reported. If data is not null, the data in data will be copied from the CPU side to the buffer object on the GPU side. If data is null, after executing this API, the buffer object will still be allocated size-sized memory, but this memory has not been initialized, that is, undefine will be stored in it. The values ​​stored in data must be aligned according to the alignment requirements of the CPU side. The fourth parameter was just mentioned when bindbuffer created the buffer object, which refers to the usage of the buffer object, that is, the data storage method of the buffer object. As I just said, the initial state of the usage of the buffer object is GL_STATIC_DRAW. Explain what GL_STATIC_DRAW is. GL_STATIC_DRAW means that the program implies that this buffer object will only be assigned once, Then there may be multiple read calls in the GPU. In addition to GL_STATIC_DRAW, there are two usages, namely GL_DYNAMIC_DRAW, which means that the program implies that the buffer object may be assigned multiple times, and may be read and called multiple times in the GPU. And GL_STREAM_DRAW, which means that the program implies that this buffer object will only be assigned once, and then it will only be read and called a few times in the GPU. The usage of usage is only applicable to the hint of performance. By passing in this hint, the GPU driver can better choose the way and location to store the buffer object, and better optimize the read and write efficiency of the buffer object. The usage does not affect the function used, no matter which usage is used, the buffer object can be used normally. If other parameters other than these three enums are passed in, an INVALID_ENUM error will be reported. This function has no output parameters, but there are several situations where errors will occur. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE, and if the target does not correspond to a If the valid buffer object, or the target corresponds to the illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. It means that the program implies that this buffer object may be assigned multiple times, and may be read and called multiple times in the GPU. And GL_STREAM_DRAW, which means that the program implies that this buffer object will only be assigned once, and then it will only be read and called a few times in the GPU. The usage of usage is only applicable to the hint of performance. By passing in this hint, the GPU driver can better choose the way and location to store the buffer object, and better optimize the read and write efficiency of the buffer object. The usage does not affect the function used, no matter which usage is used, the buffer object can be used normally. If other parameters other than these three enums are passed in, an INVALID_ENUM error will be reported. This function has no output parameters, but there are several situations where errors will occur. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE, and if the target does not correspond to a If the valid buffer object, or the target corresponds to the illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. It means that the program implies that this buffer object may be assigned multiple times, and may be read and called multiple times in the GPU. And GL_STREAM_DRAW, which means that the program implies that this buffer object will only be assigned once, and then it will only be read and called a few times in the GPU. The usage of usage is only applicable to the hint of performance. By passing in this hint, the GPU driver can better choose the way and location to store the buffer object, and better optimize the read and write efficiency of the buffer object. The usage does not affect the function used, no matter which usage is used, the buffer object can be used normally. If other parameters other than these three enums are passed in, an INVALID_ENUM error will be reported. This function has no output parameters, but there are several situations where errors will occur. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE, and if the target does not correspond to a If the valid buffer object, or the target corresponds to the illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. Then it will only be read and called a small number of times in the GPU. The usage of usage is only applicable to the hint of performance. By passing in this hint, the GPU driver can better choose the way and location to store the buffer object, and better optimize the read and write efficiency of the buffer object. The usage does not affect the function used, no matter which usage is used, the buffer object can be used normally. If other parameters other than these three enums are passed in, an INVALID_ENUM error will be reported. This function has no output parameters, but there are several situations where errors will occur. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE, and if the target does not correspond to a If the valid buffer object, or the target corresponds to the illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. Then it will only be read and called a small number of times in the GPU. The usage of usage is only applicable to the hint of performance. By passing in this hint, the GPU driver can better choose the way and location to store the buffer object, and better optimize the read and write efficiency of the buffer object. The usage does not affect the function used, no matter which usage is used, the buffer object can be used normally. If other parameters other than these three enums are passed in, an INVALID_ENUM error will be reported. This function has no output parameters, but there are several situations where errors will occur. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE, and if the target does not correspond to a If the valid buffer object, or the target corresponds to the illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE. If the target does not correspond to a legal buffer object, or the target corresponds to an illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine. In addition to the error that the incoming target or usage is illegal, it will report INVALID_ENUM, and if the size is negative, it will report GL_INVALID_VALUE. If the target does not correspond to a legal buffer object, or the target corresponds to an illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. And because a large block of memory needs to be allocated in the GPU here, there may be insufficient memory, and when the memory is insufficient, an error of GL_OUT_OF_MEMORY will be reported. Explain GL_OUT_OF_MEMORY here. This error code shows that there is not enough memory in the GPU to execute this command. This kind of error, in addition to being marked in the GPU driver, will also cause the state of GL to be undefine.

If there is data in the buffer object before executing this API, all the previous data will be deleted first, and then new data will be loaded into the buffer object. After executing this API, the status of the buffer object will be updated, BUFFER_USAGE will be set as the fourth parameter passed in by this API, and BUFFER_SIZE will be set as the second parameter passed in.

void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);

The function of this API is similar to that of glBufferData just now. As the name suggests, the API just now is to pass in data to the buffer object, and this glBufferSubData is to pass in data to a part of the buffer object.

The first input parameter of this function is the same as the first input parameter of glBufferData, which is used to specify the type of bufferobject. If a parameter other than GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER is passed in, an INVALID_ENUM error will be reported. If the target does not correspond to a legal buffer object, or the target corresponds to an illegal buffer object 0, then a GL_INVALID_OPERATION error will be reported. The second, third and fourth input parameters mean: starting from the beginning of the buffer object, offset by offset positions, starting from this position, a piece of space with a length of size units, using data The memory data in the pointed CPU is overwritten. The units of offset and size are machine units. If offset or size is negative, or offset + size is greater than BUFFER_SIZE, an INVALID_VALUE error will occur. The values ​​stored in data must be aligned according to the alignment requirements of the CPU side.

This function has no output parameters. Used to update a part of the buffer object, or all data. Since the space of the buffer object has been allocated through glBufferData, no new space will be allocated here, and the error of GL_OUT_OF_MEMORY will not appear.

There is one thing to note: through this API, all the data of the buffer object can also be overwritten, just set offset to 0, size to GL_BUFFER_SIZE, and data to point to a new set of data. If we want to update all the content in the buffer object, in theory, we can directly reassign by calling glBufferData again. But it is recommended to use glBufferSubData to manipulate such things. Because glBufferData will involve reallocation of memory, which will consume more resources, while glBufferSubData will not involve reallocation of memory.

void glDeleteBuffers(GLsizei n, const GLuint * buffers);

When the buffer is no longer needed, the buffer object name can be deleted through the glDeleteBuffers API.

The input parameter of this function means that the API will delete n buffer objects. When n is less than 0, an INVALID_VALUE error will occur. Buffers save the variable names of these buffer objects. If the variable name passed in is 0, or the corresponding is not a legal buffer object, then the API will be ignored.

This function has no output parameters. When the buffer object is deleted, its content will also be deleted, and the name will be released, which can be reused by glGenBuffers. If the deleted buffer is in the bind state, then it is equivalent to executing glBindBuffer once to change the corresponding binding to binging 0, which is equivalent to no binding at all, and then delete it.

*void glBindAttribLocation(GLuint program, GLuint index, const GLchar name);

When talking about GLSL syntax, I introduced attribute, which is a storage modifier in shader and only exists in VS. OpenGL ES can get the location of a certain attribute in VS through API, and then transfer the data to VS through this location and another OpenGL ES API. It is mainly used to pass in information such as vertex coordinates and colors. So the attribute has a location that can be obtained by OpenGL ES. However, this location can be directly specified through the OpenGL ES API. If it is not specified, it will be customized by the GPU for all developers in the shader during linkProgram and has not been specified. Assign an index to the active attribute of the specified location. And glBindAttribLocation is to specify the index for the attribute through the API of OpenGL ES, which can also be said to specify the location. The API for obtaining attribute location is called glGetAttribLocation, and the API for transferring data to Attribute is called glVertexAttribPointer. Now let's talk about glBindAttribLocation first, and talk about the other two APIs later.

The first input parameter of this function is the program object, if we pass in an illegal value of the program object, an INVALID_VALUE error will appear. The second parameter is an unsigned value, which will be the index of the attribute or the location of the attribute. But this value is limited, it cannot be greater than or equal to the value of GL_MAX_VERTEX_ATTRIBS, otherwise there will be an error of GL_INVALID_VALUE, and it will also cause the link to fail. However, different from the name of shader or program and buffer object, the name of shader or program or buffer object is an unsigned non-zero integer, and the location of attribute can be 0. The third parameter is a string, which is used to save the variable name of an attribute in the vertex shader corresponding to the program. Here we will not check whether the variable name is real and valid, but will check that the variable name cannot be prefixed with gl_. It is also said in the GLSL grammar that the variable names prefixed with gl_ are reserved by the GLSL grammar, and custom variables cannot use gl_ as a prefix. If used, there will be a GL_INVALID_OPERATION error. GPU will copy the name when executing this API, that is to say, after executing this API, name, the character used to save the attribute name, can be freed.

This function has no output parameters. In addition to the error cases just mentioned, if you give a matrix attribute bindlocation, but because the matrix needs more than one location, for example, mat4 needs 4 locations, and it needs 4 consecutive locations, then the second parameter index It is the location of the first column of the matrix, index + 1 is the location of the second column of the matrix, index + 2 is the location of the third column of the matrix, index + 3 is the location of the fourth column of the matrix, if there are no 4 consecutive locations, linkprogram There is also the possibility of failure.

After this API is executed, it will take effect the next time the program is linked, that is, if the program has been linked once before, attributeA has been automatically assigned a location 1. Then use this API to bind Attribute A to location 2. After executing this API, the location of attribute A is still 1. To access this attribute using OpenGL ES API, you still need to use location 1 to access it. However, after the program is linked again, the location of the attribute becomes 2. That is to say, before the program is linked again, the execution of this API has no effect in the eyes of the developer.

We know that this API needs to be executed before linkprogram, so in fact it can even be executed before program attachShader, that is, it can be executed when the program has no associated shader. Because when we execute this command, we will only judge whether the third parameter is prefixed with gl_, and we don't care whether it is a real existence in the shader corresponding to the program, or whether it is an active attribute. Of course, if the third parameter is not an active attribute, then the bind will be ignored directly during linkprogram.

This bind is the current state of GL, which means it's not just a program's behavior. For example, we bind attribute A of a program A to location 1. If we suddenly start using program B through glUseProgram and no longer use program A, then if there is Attribute A in program B, then the location of Attribute A is also 1, this needs special attention.

Programs can also bind multiple attributes to the same location. This phenomenon is called aliasing, but the premise is that only one attribute is active in the shader of the program being used. Otherwise, a link error will appear. Compiler and linker think that there is no such aliasing by default, and do a corresponding optimization. If an attribute is bound twice to location, the previous one will be invalid. So you can't bind two locations to one attribute.

It is not allowed to bind location for built-in attributes through this API, because they will be automatically bound if necessary.

GLint glGetAttribLocation(GLuint program, const GLchar*name);

No matter assigning a location to the attribute through glBindAttribLocation, or assigning a location to the attribute automatically through the linkprogram GPU, the attribute has a location. Then the glGetAttribLocation API is OpenGL ES to get the location of an attribute in the specified VS. After getting this location, you can operate this attribute through other APIs of OpenGL ES.

The first input parameter of this function is program object, if we pass in an illegal value of program object, or the program is not successfully linked, then GL_INVALID_OPERATION error will appear. In fact, I originally thought that there should be an error of INVALID_VALUE. According to the previous practice, there should be an error of INVALID_VALUE, but whether it is the OpenGL ES Spec or the website of khronos, it is said that there will be an error of GL_INVALID_OPERATION, so everyone should pay attention here . The second parameter is a string, which is used to save the variable name of an attribute in the vertex shader corresponding to the program. Similarly, here we will not check whether the variable name is real and valid, nor will we check whether it is prefixed with "gl_". So here name only needs to be a string.

This function has output parameters, if the attribute specified by name is an active attribute in this program, then output the location of the attribute specified by name in this program when the program was linked last time. If the program is linked twice, the location of the attribute is 1 for the first time, and the location is 2 for the second link, and then execute glBindAttribLocation again to set the location of the attribute to 3. Then execute this API at this time to get this The location of the attribute is 2. If attribute is a matrix, then return the location of the first column of the matrix. If the name is prefixed with "gl_", or specifies an attribute that is not active, or even an attribute that does not exist in the current program, or the above-mentioned errors occur when the API is executed, then it will returns -1. Since 0 is also a valid attribute location, -1 is returned here.

void glEnableVertexAttribArray(GLuint index);

After obtaining the location of the attribute, before OpenGL ES and GPU actually use this attribute, you need to enable this attribute through the glEnableVertexAttribArray API. If it is not enabled, the value of this attribute cannot be accessed, for example, the value of this Attribute cannot be assigned through OpenGL ES. What's more, if it is not enabled, the GPU cannot even use this attribute when drawing through the two APIs of glDrawArray or glDrawElement because the value of the attribute cannot be accessed.

The input parameter of this function is the location of the attribute. As I just said, when OpenGL ES obtains the location of the attribute, it can operate the attribute through this location. But this value is limited, it cannot be greater than or equal to the value of GL_MAX_VERTEX_ATTRIBS, otherwise there will be an error of GL_INVALID_VALUE, the normal attribute location should be less than this value, so if you write more than this value, it must be wrong.

This function has no output parameters. By default, all developer-defined attributes are disabled. If you want to use them, you must enable them first.

void glDisableVertexAttribArray (GLuint index);

With enable, there must be disable. After the drawing is finished, you can pass glDisableVertexAttribArray disable the useless attribute, and turn off the switch of a certain attribute in the specified program. After it is turned off, the GPU cannot access the value corresponding to the attribute when drawing. The input parameter of this function is also the location of the attribute, which cannot be greater than or equal to the value of GL_MAX_VERTEX_ATTRIBS, otherwise a GL_INVALID_VALUE error will occur.

This function has no output parameters.

void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);

I did so much preparatory work just now, I got the location of the attribute, and then enable the attribute, the purpose is to pass the value to the attribute. And the most important value-passing API is glVertexAttribPoint, which is used to transfer data from OpenGL ES to VS. Before preparing to pass values ​​through this API, those actual values ​​may be stored in the CPU, such as in the form of a pointer or data, or may be stored on the GPU side through the buffer object created and assigned just now. Regardless of where it is stored, you can assign values ​​to attributes through this API. Then the assigned attribute represents the coordinates or colors of several vertices and other information.

The first input parameter of this function is the location of the attribute, which cannot be greater than or equal to the value of GL_MAX_VERTEX_ATTRIBS, otherwise a GL_INVALID_VALUE error will occur. The second parameter is size. Recall what I said when I talked about GLSL grammar. At that time, if I want to draw a triangle, I need to set three vertices. The three vertices will be calculated by VS to get the real three vertices. The world coordinate value, and then lighten these three vertices to generate countless vertices that form a triangle. These vertices will pass through PS, and the color values ​​​​of these vertices will be obtained through PS operations. Therefore, VS is executed three times, and the function of VS is used in these three times. The difference is that the input of these three functions is different, resulting in different output results. The input of the VS function is the attribute equivalent. Therefore, the value passed to the attribute through glVertexAttribPoint will be divided into multiple units, each vertex claims a unit, and each unit contains size variables. The size defaults to 4, that is to say, if you want to draw a triangle, you need to pass in three units and multiply each unit by 4 values, that is, you need to pass in 12 values ​​through this API. Then the attribute of each vertex claims 4 values ​​in turn, so the attributes of the three vertices are ready. size can only be 1, 2, 3, 4, and it is relatively easy to understand, because the largest variables in GLSL are vec4 and mat4. And if it is an attribute of mat4, it will be split into attributes according to the column, so an attribute has at most 4 components. If the size does not use any of these four values, an error of INVALID_VALUE will appear. The third parameter is type, which is used to specify the data type of the stored data, such as GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, or GL_FLOAT, the default is GL_FLOAT, if type is not one of these values, Then an INVALID_ENUM error occurs. The fourth parameter normalized means that if the value assigned to the attribute is a fixed-point value or an integer value, when it is passed to the attribute and converted into a float value, whether it needs to be normalized. Normalization is the signed value converted to ( -1,1), unsigned values ​​are converted to (0,1). If true, normalization is required, and if false, normalization is not required. Generally, I pass the vertex or color coordinates directly using the normalized value, so that normalization is not needed and the burden on the GPU is reduced. The fifth parameter stride is the interval. For example, in order to draw a triangle just now, 12 values ​​need to be passed in. Then you can directly create an array of 12 values, or you can create an array of 15 values, of which the 5th, 10th, and 15th values ​​​​are useless values, and the 12th values ​​​​of 1234, 6789, and 1112131415 It is useful, then write size as 4 and stride as 5, GPU will know that 5 values ​​are read as one unit, then the first four are effective values, and use the first four to assign values ​​to attribute. This is what stride does. If an array of 12 values ​​is directly created and passed in, the size is written as 4, and the stride is also 4, or the stride can be 0. Because if it is 0, the GPU also knows to assign size values ​​​​to the first attribute first, and then assign the next size values ​​to the second attribute. stride is 0 by default, if stride is less than 0, an INVALID_VALUE error will occur. The last parameter pointer is very important, it can be divided into two cases, if the actual data is stored on the CPU side, then pointer is a pointer or array address pointing to the actual data storage location. If the actual data is stored in the VBO of the GPU, then the pointer will pass in an offset, which means starting from a certain bit of the VBO, reading the stride or size from the subsequent values ​​as a unit, and using the size values ​​as valid data ,

This function has no output parameters.

void glVertexAttrib*f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);

In addition to the above API glVertexAttribPoint used to assign values ​​to attributes, there are also some simple APIs to assign values ​​to attributes. These APIs are glVertexAttrib*, which contains three parts. The first is a number, which can be 1 or 2 or 3 or 4. The second is a letter, which may or may not have f, and the third is v, which may or may not have v. Let me talk about the difference between this set of APIs and the one just now. For the API just now, we can pass in three sets of different units for three vertices, resulting in different attributes of the three vertices. Using this set of APIs, the attributes of all vertices are the same. same. So through this API, after specifying an attribute, at most four values ​​are passed in to assign values ​​to the attributes of all vertices.

Through the description of the API just now, we know that each attribute is composed of at most 4 values, so if the attribute is composed of 1 value, then use glVertexAttrib1f to pass in 1 value, then this value is just assigned to this attribute, but if attribute is composed of 4 values, and use glVertexAttrib1f to pass in 1 value, then the other three values ​​use the default value, the second and third are 0, and the fourth bit is 1; and so on, if you use glVertexAttrib2f to pass Enter 2 values, then the other two values ​​use the default value, the third is 0, and the fourth bit is 1; if you use glVertexAttrib3f to pass in 3 values, then the other value uses the default value, and the fourth bit is 1 . If you use an API ending with f, such as glVertexAttrib4f, it means that the value passed in is float type. If you use an API ending with v, such as glVertexAttrib4fv, the only difference between using glVertexAttrib4f is that the incoming parameters are different. Using glVertexAttrib4f, you need To pass in four values, using glVertexAttrib4fv, you only need to pass in a pointer containing four values.

The first input parameter of this function is the location of the attribute, which cannot be greater than or equal to the value of GL_MAX_VERTEX_ATTRIBS, otherwise a GL_INVALID_VALUE error will occur. For the following input parameters, the parameters passed in are different according to the name of the API. In short, the corresponding parameters will be passed in according to the variable name of the API.

This function has no output parameters. Using this set of APIs will result in the same Attribute for each vertex, but each vertex can also support MAX_VERTEX_ATTRONS Attributes. The attribute of matrix is ​​also supported.

*GLint glGetUniformLocation(GLuint program, const GLchar name);

When talking about glLinkProgram, when talking about linkprogram, it will assign a location to the attribute without specified index, and also initialize the active uniform defined by the developer in all shaders to 0, and assign it an address. After each relink, the location of the uniform will be released, and then a new location will be reassigned. Of course, the new location may be the same as the old location. The glGetUniformLocation API is used to obtain the location of the uniform.

Regarding the definition and usage of uniform, it has been mentioned in detail in GLSL grammar. Here are a few more simple points. First, uniform exists in VS and PS, which is different from attribute only in VS. Second, Uniform is the same for different point values, different from attribute, which can be the same for each point (such as when passing in glVertexAttrib*), or it can be different (such as using glVertexAttribPoint). Third, the uniform value is read-only. 4. If the compiler and linker think that the uniform will be used or are not sure whether it will be used, they will think that the uniform is active. 5. Once their values ​​are loaded, they will remain after the program starts to use until the program is relinked.

The first input parameter of this function is the program object, if an illegal value of the program object is passed in, an error of INVALID_VALUE will appear. If this program is not successfully linked, then there will be an error of GL_INVALID_OPERATION. The second parameter is a string, which is used to save the variable name of a uniform corresponding to the program. Similarly, here we will not check whether the variable name is real and valid, nor will we check whether it is prefixed with "gl_". So here name only needs to be a string, but note that this string cannot contain spaces.

This function has output parameters, if the name is prefixed with "gl_", or the specified is not an active uniform, or even an attribute that does not exist in the current program, or the above mentioned occurs when the API is executed Those errors, then -1 will be returned. Remember attribute can be vec4 or mat4. Unform can even be an array, a structure, etc. Here, the name cannot be set as a structure, a structure array, or a part of a vector or matrix, so as to directly obtain their corresponding addresses, only through . or [] The form is passed to a member in the array or structure, and then the address of the member is obtained. For example, the address of the first element in the array uses the array name plus [0] as the name to obtain the address of the first element. The address of the array is the address of its first element, or the address obtained by directly passing the array name through name.

*void glUniform*iv(GLint location, GLsizei count, const GLint value);

This is to assign a value to the uniform, which is similar to glVertexAttrib*, which is assigned after the location is obtained. But there are certain differences.

The set of glUniform* APIs is divided into 3 types. According to the name of the API, the parameters passed in are different.

The first is to pass data to Uniform directly in the form of value, such as glUniform1f, glUniform4f, glUniform4i, etc. This type of API is mainly used to assign values ​​to uniforms when the uniforms in the shader are float, int, or vec types, and not array.

The first input parameter of this API is the location of the attribute, which must be a legal uniform location or -1, otherwise a GL_INVALID_OPERATION error will occur. If location is -1, then this API will be ignored and will not change any uniform values.

The following parameters are different according to the API. For example, glUniform1f has only 2 parameters in total. The first parameter has been mentioned just now, and the second parameter is a value, which is used to assign the value to uniform.

For example, glUniform4f has 5 parameters in total. Except for the first parameter just mentioned, the remaining four parameters are used to assign values ​​to the uniform.

The second is to pass the data to the uniform in the form of a pointer, such as glUniform1fv, glUniform4fv, glUniform4iv, etc. This type of API is mainly used to assign values ​​to uniforms when the uniforms in the shader are float, int, and vec types, and the uniforms are allowed to be arrays.

The API input parameters of this type are all the same. The first input parameter is also the location of the attribute, it must be a legal uniform location or -1, otherwise a GL_INVALID_OPERATION error will occur.

The meaning of the second parameter and the third parameter is that count means that the specified uniform array is to be changed. It consists of several array elements. It can be assigned all or part of the array, and value refers to the actual storage to be assigned to The address of the value of the uniform. If count is greater than 1, but the pointed uniform is not an array, GL_INVALID_OPERATION error will occur, and the assignment to unform is invalid. If count is less than 0, a GL_INVALID_VALUE error occurs.

For example, glUniform1fv, and the specified uniform is an array with three elements, then the second parameter count is 3, and value is a pointer to a block of memory with 3*1 float values.

Another example is glUniform4iv, and the specified uniform is an array with five elements, then the second parameter count is 5, and value is a pointer to a piece of memory with 54 int values. If value points to a larger memory, then values ​​beyond 5 4 int values ​​are ignored.

The third is to pass data to uniform in the form of pointers, such as glUniformMatrix2fv and glUniformMatrix4fv. The difference between it and the second API is that this type of API is mainly used when the uniform in the shader is a matrix type, and it also supports the uniform as an array.

The API input parameters of this type are also the same. The first input parameter is also the location of the attribute, it must be a legal uniform location or -1, otherwise a GL_INVALID_OPERATION error will occur.

The meaning of the second parameter and the fourth parameter is that count means that the specified uniform is composed of several array elements, and value is the address that actually stores the value to be assigned to the uniform.

For example, glUniformMatrix2fv, and the specified uniform is an array with 3 elements, then the second parameter count is 3, and value is a pointer to a block of memory with 3 2 2 float values.

Another example is glUniformMatrix4fv, and the specified uniform is an array with five elements, then the second parameter count is 5, and value is a pointer to a piece of memory with 5 4 4 float values .

The third parameter transpose must be GL_FALSE, which means that when assigning a value to the matrix, the value is assigned in units of columns, and the second column is assigned after the first column is assigned. If other parameters are used, the error of GL_INVALID_VALUE will appear, and the assignment to uniform will fail.

So to summarize glUniform and glAttrib , there are 3 differences. 1. attribute can have different values ​​for each point, while uniform has the same value for each point. 2. If the attribute is an array, then the assignment should also be assigned in units of digital elements, which can only be assigned to one array element at a time, and if the uniform is an array, all the values ​​in the array can be assigned at one time. 3. Although both attribute and uniform support the matrix type, when passing values ​​to attribute through glVertexAttribPoint or glVertexAttrib, although multiple units can be transferred at a time, each unit can only transfer up to 4 floats, while glUniform can directly transfer one A value of type matrix.

In the first and second APIs, there is an API for assigning values ​​to samples, glUniform1i and glUniform1iv, which are used to assign values ​​to samples and sample arrays respectively. We know that sample is a special uniform, which is equivalent to a uniform whose variable type is int, and is used to save texture objects. If other APIs are used, GL_INVALID_OPERATION error will occur due to different size or type.

Just mentioned the float and int types, but if the uniform is bool type, then it doesn't matter whether you use glUniform i or glUniform f. GL will automatically convert the type. false if the input argument is 0 or 0.0, true otherwise.

Having said so much about the glUniform* API, when passing values, you must select the appropriate API strictly according to the size and type of the uniform. If the size does not match, for example, the uniform is vec3, but glUniform2fv is used, GL_INVALID_OPERATION will appear mistake. And if the types do not match, except for the bool uniform which will be automatically converted, others will not be automatically converted. For example, if the uniform is vec3, but glUniform3iv is used, the error of GL_INVALID_OPERATION will appear. If such an error occurs, the assignment of uniform will also be invalid.

This function has no output parameters. In addition to the errors mentioned above, there will be GL_INVALID_OPERATION errors if no program is currently being used.

These uniforms will keep the assigned values ​​until the program is linked again, because when linked again, these uniform values ​​will be initialized to 0 again.

The above APIs are the main APIs for OpenGL ES to transfer attribute and uniform information to GPU Shader. In addition to the APIs about attribute and uniform in OpenGL ES, there are many APIs used for query services, such as glGetBufferParameteriv, which is used to query the parameters of the specified buffer, and the query object can only be the currently used VBO Or IBO, the query is the GL_BUFFER_SIZE or GL_BUFFER_USAGE of the buffer. For example, the API glIsBuffer is used to query whether the incoming parameter is a legal buffer. For example, API glGetVertexAttrib and glGetActiveAttrib are used to query the attribute of attribute, while API glGetVertexAttribPointerv is used to query the address of attribute. For example, API glGetActieUniform is used to query the properties of uniform, and API glGetUniform is used to query the value of specified uniform.

The vertex information is passed in, the shader is ready, and the API can be used to trigger the GPU to start rendering.

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);

In EGL, we created a drawing buffer through eglCreateWindowSurface, and the size of this buffer is the size of the screen resolution. However, although the size of this buffer has been determined, it does not mean that when we use this buffer for drawing, we must draw exactly according to the size of this buffer. It's like we have a piece of A4 drawing paper, then we want to draw a sun and a house, we will draw it twice, the first time we will draw the sun in the upper left corner of the drawing paper, and the second time we will draw it in the upper left corner of the drawing paper Draw a house in the lower right corner. Then when drawing for the first time, we only need to set the drawing area in the upper left corner, and for the second drawing, set the drawing area in the lower right corner. The glViewPort API is used to set the drawing area.

Setting the drawing area will have a great impact on the drawn results. Remember that when we pass glVertexAttribPoint to the attribute in the shader, it is assumed that this attribute is just a variable used to represent the vertex coordinates. Then we said that we can choose whether to normalize or not when passing the value. I also said that the value I usually pass in is a normalized value. For the vertex coordinates, it is like I passed in a (0.5, 0.5 , 0, 1), this point looks like the middle point of the drawing paper, but if our screen resolution is 1080 720, then the size of the drawing buffer we generate through egl is 1080 720, and then if we set the drawing through glViewport The area starts from the lower left corner of the drawing paper, and the width and height are 1080 x 720. Then the drawing area is the entire drawing paper, and our point (0.5, 0.5, 0, 1) is the point with coordinates (540, 360). However, if the drawing area we set through glViewport starts from the middle point of the drawing paper, the width and height are 540 x 360. Then the drawing area is the upper right corner of the drawing paper, and our point (0.5, 0.5, 0, 1) is the point with coordinates (810, 540). For another example, if we want to draw a line from the point (0.5, 0.5, 0, 1) to the point (0.5, 1, 0, 1), and the drawing area we set through glViewport is from the drawing paper Starting from the lower left corner, the width and height are 1080 x 720. That is to say, the drawing area is the entire drawing paper, so the line we draw is from (540, 360), to (540, 720), and the length is 360 units. Suppose the drawing area we set through glViewport starts from the middle point of the drawing paper, and the width and height are 540 x 360. Then the drawing area is the upper right corner of the drawing paper, then the line we draw is from (810, 540), to (810, 720), and the length is 180 units. The length is half as long as before.

Therefore, the drawing area is different, which directly leads to the drawing position and the size of the drawing picture.

The first two input parameters of this function represent the position of the lower left corner vertex of the drawing area in the entire drawing buffer, and the initial value is (0, 0). The last two input parameters represent the width and length of the drawing area, and the initial value is the size of the screen resolution (so if this API is not called, it is equivalent to calling glviewport(0, 0, screen_width, screen_height)). The latter two parameters representing width and length are limited and cannot be negative, otherwise GL_INVALID_VALUE error will occur. Of course, they can’t be too large. The GPU has set a maximum value for them, and you can query them through the glGet API with the parameter GL_MAX_VIEWPORT_DIMS. If it exceeds, the GPU will automatically clamp this value. Of course, GL_MAX_VIEWPORT_DIMS will definitely be greater than or equal to the actual resolution of the screen.

This function has no output parameters. The normalized vertex coordinates will calculate the final vertex coordinates on the screen according to the input parameters. For example, the vertex coordinates after normalization are (0.5, 0.5), and the drawing area is from (0, 0) to (1080, 720), then the final point displayed on the screen is 0.5 1080 + 0.5 = 540.5, 0.5 720 + 0.5 = 360.5.

Through the above APIs, we have made sufficient preparations for drawing pictures, passed in the values ​​that need to be passed in, and set the drawing area. Then we will talk about three drawing APIs below.

void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

glClear, used to clean up the drawing buffer. Because the drawing buffer we created through egl is actually a piece of memory, but this memory will not be initialized when it is just created, so depending on the platform, the things stored in it may be different, just like we have prepared A piece of drawing paper, but there may be something on this drawing paper, these things are uncertain, so we first need to paint the background color of this drawing, and we may hope that the background color of this drawing paper is white, and there are also It may be desired to be black or other colors, and the glClearColor API is to set a color as the color used to clean up the drawing buffer.

This function has a total of 4 input parameters, which are the four values ​​of rgba, which are used to determine a specific color for cleaning the drawing buffer. The initial values ​​of these 4 input parameters are all 0. These 4 values ​​can be filled in freely when inputting, but they will be clamped by the closed interval [0,1].

This function has no output parameters. The color determined by this API will be used in glClear to clean up the drawing buffer.

void glClear(GLbitfield mask);

This is the first drawing API we will learn, which is used to clear the drawing buffer with a preset value. For example, if we want to clear the color of the drawing buffer, we have already determined a color through the glclearcolor API just now. , and then through this API, you can call the GPU to clean up the color of the drawing buffer according to the color you just set.

The input parameter of this function is a mask value, which is used to specify which part of the drawing buffer to be cleaned, because the drawing buffer is mainly divided into three buffers, color buffer, depth buffer, and stencil buffer. Just now we have learned how to set the default color to clean up the drawing buffer, and then we can use that color to clean up the drawing buffer as long as we pass in GL_COLOR_BUFFER_BIT here. And there are two other APIs, glClearDepth and glClearStencil, which are used to set the default depth and stencil values ​​for cleaning the drawing buffer, and then need to pass in GL_DEPTH_BUFFER_BIT and GL_STENCIL_BUFFER_BIT here to clear the depth and stencil of the drawing buffer. If other values ​​other than these three values ​​are passed in, a GL_INVALID_VALUE error will occur. Since depth and stencil are not required by every OpenGL ES program, we will explain these APIs in later courses.

This function has no output parameters. The API will clean up the corresponding modules in the drawing buffer according to the parameters passed in, such as cleaning up the color, depth, and stencil modules. You can only clean up one module, or you can clean up multiple modules through an API call. But if the drawing buffer does not contain this module, for example, many GPU formats do not support stencil, then glClear stencil will have no effect. There are many other GL states that will affect the results of glclear, such as scissor test. The meaning of scissor is to set a small drawing area in the drawing area. If the screen resolution is 1080,720, the viewport is (0,0) to (1080, 720), and then when OpenGL ES opens scissor, then set the small drawing area to (0,0) to (1,1) through glScissor, then execute glClear, it will not clear a whole block Draw the buffer, but only clear the small drawing area. Another example is glColorMask, glColorMask is to limit those color components in the drawing buffer that can be written, the default is that all four color components can be written, but it can also limit that only the r channel can be written, and then if glClearColor is set to (1,1 ,1,1), that is, it is ready to be cleared to white, but since only the r channel can be written, it will actually be cleared to red. In addition to the first time, dither will also affect glClear

glClear will also clear the color/depth/stencil buffer of multisample.

void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);

When the PS is over, Mask can limit whether the color, depth, and stencil can be written to the corresponding buffer. For example, this glColorMask API is used to control the write permission of the color buffer R\G\B\A channel.

This function has a total of 4 input parameters, which are the four values ​​of rgba, which are used to determine whether the four channels of RGBA have write permission. If the input is GL_TRUE, it means that the channel has write permission. In the initial state, all four channels of RGBA are writable by default. When GL_FALSE is passed, the R channel of all color buffers will not be writable. It is impossible to control the authority of only one channel, or to control four channels at once.

This function has no output parameters.

void glDrawArrays(GLenum mode, GLint first, GLsizei count);

If glClear just cleared the drawing buffer, although it is also an operation on the drawing buffer, changing the color equivalent value in the drawing buffer, then this glDrawArray is a real drawing in the drawing buffer using the vertex information we passed in. API. Remember when we talked about attribute, we said that if we want to draw a triangle, we need to pass in information such as the position and color of three points. This information will be stored in the attribute. So through the assignment of attribute, the GPU has saved the information of three vertices. Then we need to pass in parameters through the glDrawArray API to tell the GPU which vertices of these three vertices are used for drawing, and how to assemble the primitives for the points used for drawing.

Let's talk about the second and third input parameters of this function first. What they mean is that drawing through this API will use those vertices saved in the GPU, starting from the first vertex and ending with first+count vertices, using count vertices as the drawn points. If there are other vertices stored in the GPU, those vertices will be ignored in this drawing. Among them, first and count cannot be less than 0, otherwise the behavior of undefine will occur, and an error of GL_INVALID_VALUE will be generated. The first input parameter of this function is an enumeration value, which is used to specify what kind of primitives to draw through these vertices. It can be GL_POINTS, which means that these vertices will be drawn as points on the drawing, or GL_LINE_STRIP, which means a continuous line segment, the first vertex is used as the starting point of the first line segment, and the second vertex is used as the first line segment The end point of a line segment and the starting point of the second line segment, and so on, the i-th vertex is used as the end point of the i-1th line segment and the starting point of the i-th line segment. The last vertex, as the end point of the previous line segment. If you just use a vertex to draw, then this drawing API is meaningless. In this way, in the end, what is drawn is either nothing, or a line segment, or a polyline. It can also be GL_LINE_LOOP, which is basically the same as GL_LINE_STRIP, the only difference is that the last vertex will be connected with the first vertex. If you just use a vertex to draw, then this drawing API is meaningless. In this way, in the end, what is drawn is either nothing, or a line segment, or a closed polyline. GL_LINES is also used to draw line segments, but unlike the previous two, the line segments drawn this time are not continuous. After specifying this format, the first and second points will form a line segment, the third and fourth points will form a line segment, and these two line segments are independent, and so on, if there are 2i or 2i in total +1 point, i line segments will be drawn. It can be seen that if the number of vertices used for drawing is odd, the last vertex will be ignored. GL_TRIANGLE_STRIP, Used to draw triangles, the first, second, and third points will be connected to form the first triangle. Pay attention to the order of connections. The connection methods of 123 and 321 are completely different in graphics. Clockwise or counterclockwise to judge whether the primitive is front or back, and then it will be used for culling. However, removing these features is not required by every OpenGL ES program. We will explain these APIs in later courses. Here we must first know that the order of connection is very important. So here, 123 is formed into a triangle, and then 324 is formed into a triangle, which shares a side with the first triangle, and then 345 is formed into a triangle, which has a side with the second triangle is shared. So with this format, what we get is a bunch of triangles that share sides, and they are either all clockwise or all counterclockwise. If you only use one or two vertices to draw, then this drawing API is meaningless. GL_TRIANGLE_FAN is also used to draw triangles, and as the name suggests, it is to draw a fan-shaped triangle. Then 123 will form a triangle, 134 will form the second triangle, 145 will form the third triangle, and so on, 1, i-1, i will form the last triangle, and these triangles also have one side shared . So with this format, what we get is also a bunch of triangles that share sides, and they are either all clockwise or all counterclockwise. If you only use one or two vertices to draw, then this drawing API is meaningless. The last format is GL_TRIANGLES. It is conceivable that it is also used to draw triangles, and it draws independent triangles. That is, 123 forms a triangle, and 456 forms another triangle. 3i-2, 3i-1, 3i form the i-th triangle, if the number of drawn vertices is not an integer multiple of three, then one or two extra vertices will be ignored. In this format, the drawn triangles are not necessarily all clockwise or all counterclockwise. If the mode is not the above parameters, the error of GL_INVALID_ENUM will appear. It is also used to draw triangles, and as the name suggests, it is to be drawn into a fan-shaped triangle. Then 123 will form a triangle, 134 will form the second triangle, 145 will form the third triangle, and so on, 1, i-1, i will form the last triangle, and these triangles also have one side shared . So with this format, what we get is also a bunch of triangles that share sides, and they are either all clockwise or all counterclockwise. If you only use one or two vertices to draw, then this drawing API is meaningless. The last format is GL_TRIANGLES. It is conceivable that it is also used to draw triangles, and it draws independent triangles. That is, 123 forms a triangle, and 456 forms another triangle. 3i-2, 3i-1, 3i form the i-th triangle, if the number of drawn vertices is not an integer multiple of three, then one or two extra vertices will be ignored. In this format, the drawn triangles are not necessarily all clockwise or all counterclockwise. If the mode is not the above parameters, the error of GL_INVALID_ENUM will appear.

This function has no output parameters. Because drawing a picture requires not only vertex coordinate color information, but also information such as the normal vector of the vertex, and these information often use default values. Therefore, if the information is not assigned, or the attribute is not enabled by glEnableVertexAttribArray, the default value will be used to participate in the drawing.

If the currently used program is not a legal program, the drawn result is undefined. But no other errors appear.

There is also a possibility that it will cause errors. We already know that egl will create a drawing buffer for GL that can be used for drawing. In fact, OpenGL ES can also create a drawing buffer by itself, which we call FBO and RBO. There are many APIs about FBO and RBO, but because these APIs are not needed by every OpenGL ES program, we will put these APIs in It will be explained later in the course. And if you use the drawing buffer created by OpenGL ES to draw, but if the drawing buffer created by OpenGL ES is wrong, then the error of GL_INVALID_FRAMEBUFFER_OPERATION will appear. Here we explain GL_INVALID_FRAMEBUFFER_OPERATION, GL_INVALID_FRAMEBUFFER_OPERATION is the last glError flag, we have said the others before, when the OpenGL ES program reads from a framebuffer without complete, or draws into it, it will report this error. And this kind of error can be ignored, except that the operation to be performed by this API is not successfully executed, and the GPU driver is marked, other aspects will not have any impact. Whether this framebuffer is complete or not can be queried through the glCheckFramebufferStatus API. If the query result is not GL_FRAMEBUFFER_COMPLETE, it means that the queried framebuffer is not complete.

void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices);

The function of this API is basically the same as that of glDrawArrays just now, the only difference is that the way of selecting the vertices used for drawing is different. If there are a total of 10 vertices stored in the GPU, then through glDrawArrays, we will select a starting point from these 10 vertices, and then determine a length, so we get several consecutive vertices. And glDrawElements is more flexible. Developers can specify some discontinuous vertices through it, so they need to pass in an array, which stores the index of the vertices. For example, we can pass in a 1357, that is, choose the first, third and third 5 The seventh vertex, the first drawn vertex is 1, and so on, for drawing. Other vertices in the GPU will be ignored in this drawing.

The first input parameter of this function is the same as the first input parameter of glDrawArrays, which is used to specify the mode type of the primitive to be drawn. If the mode is not the above parameters, the error of GL_INVALID_ENUM will appear. We have already talked about it in detail just now, so I won't go into detail here. The second input parameter is the same as the third input parameter of glDrawArrays, count, which is used to specify how many vertices are used as the drawing points. count cannot be less than 0, otherwise it will lead to undefine behavior and GL_INVALID_VALUE error. The last parameter indices is very important, it can be divided into two cases, if the actual data is stored on the CPU side, then indices is a pointer or array address pointing to the actual data storage location. If the actual data is stored in the IBO of the GPU, then the indices will pass in an offset, which means starting from a certain bit of the IBO, and reading count values ​​from the subsequent values ​​as the value of the indices. The third input parameter means that the variable type used to store the vertex index memory is type. type must be GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT. Otherwise, a GL_INVALID_ENUM error will occur.

This function has no output parameters. The wrong way is basically the same as glDrawArrays.

If the currently used program is not a legal program, the drawn result is undefined. But no other errors appear.

If the drawing buffer is wrong, then there will be a GL_INVALID_FRAMEBUFFER_OPERATION error.

void glLineWidth(GLfloat width);

According to the first parameter mode of the above two APIs glDrawArrays and glDrawElements, if the mode is GL_LINE_LOOP, GL_LINE_STRIP, GL_LINES. Then what is rasterized is a line segment, and the thickness of the line segment is set by the glLineWidth API.

This function has only one input parameter width, which defaults to 1. If it is less than or equal to 0, a GL_INVALID_VALUE error will occur.

This function has no output. In fact, the final line width is floor(width), that is, if the input is 1.9, then the actual width is 1. And if the input is 0.8, the floor will be 0, but the actual width is still 1. If ∆X>∆Y, then the line width of width is reflected on the column, that is, the vertical dimension is width. Otherwise, it is reflected in the line.

In addition, how many line widths are supported is determined by the hardware. Only the line width of size 1 is mandatory and must be supported. Whether other sizes are supported can only be queried by passing in GL_ALIASED_LINE_WIDTH_RANGE through the API glGet. In fact, the final line width will also be clamped to within this supported size.

If you query through GL_LINE_WIDTH, the number returned is the number set by the glLineWidth API, not the actual number (such as the number after the floor).

Guess you like

Origin blog.csdn.net/u012124438/article/details/128422866