Unity Glsl Link Error: Warning: Output of Vertex Shader 'vs_texcoord0' Not Read by Fragment Shader

Shaders

Getting-started/Shaders

As mentioned in the Hello Triangle chapter, shaders are niggling programs that rest on the GPU. These programs are run for each specific section of the graphics pipeline. In a basic sense, shaders are nil more than programs transforming inputs to outputs. Shaders are also very isolated programs in that they're non immune to communicate with each other; the merely communication they have is via their inputs and outputs.

In the previous chapter we briefly touched the surface of shaders and how to properly utilize them. We volition now explicate shaders, and specifically the OpenGL Shading Language, in a more full general fashion.

GLSL

Shaders are written in the C-like language GLSL. GLSL is tailored for use with graphics and contains useful features specifically targeted at vector and matrix manipulation.

Shaders always brainstorm with a version declaration, followed by a listing of input and output variables, uniforms and its main part. Each shader'due south entry signal is at its main function where we process any input variables and output the results in its output variables. Don't worry if you lot don't know what uniforms are, we'll get to those shortly.

A shader typically has the post-obit structure:

                      #version version_number in blazon in_variable_name; in type in_variable_name;  out type out_variable_name;    compatible blazon uniform_name;    void main() {   // process input(s) and do some weird graphics stuff   ...   // output processed stuff to output variable   out_variable_name = weird_stuff_we_processed; }                  

When we're talking specifically almost the vertex shader each input variable is also known as a vertex aspect. At that place is a maximum number of vertex attributes we're allowed to declare limited by the hardware. OpenGL guarantees there are ever at least 16 4-component vertex attributes available, but some hardware may let for more which you tin can remember by querying GL_MAX_VERTEX_ATTRIBS:

                      int nrAttributes; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes); std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;                  

This oftentimes returns the minimum of sixteen which should be more than plenty for most purposes.

Types

GLSL has, like any other programming language, data types for specifying what kind of variable nosotros want to work with. GLSL has nigh of the default basic types we know from languages like C: int, float, double, uint and bool. GLSL as well features two container types that nosotros'll be using a lot, namely vectors and matrices. Nosotros'll discuss matrices in a later chapter.

Vectors

A vector in GLSL is a 2,3 or 4 component container for any of the basic types simply mentioned. They can accept the following form (north represents the number of components):

  • vecn: the default vector of northward floats.
  • bvecn: a vector of n booleans.
  • ivecn: a vector of north integers.
  • uvecn: a vector of n unsigned integers.
  • dvecn: a vector of northward double components.

Most of the fourth dimension we volition be using the basic vecn since floats are sufficient for most of our purposes.

Components of a vector can be accessed via vec.10 where x is the first component of the vector. You tin use .x, .y, .z and .westward to access their showtime, second, 3rd and fourth component respectively. GLSL too allows you to use rgba for colors or stpq for texture coordinates, accessing the aforementioned components.

The vector datatype allows for some interesting and flexible component pick called swizzling. Swizzling allows us to apply syntax like this:

                      vec2 someVec; vec4 differentVec = someVec.xyxx; vec3 anotherVec = differentVec.zyw; vec4 otherVec = someVec.xxxx + anotherVec.yxzy;                  

You can use any combination of upward to 4 letters to create a new vector (of the same type) as long equally the original vector has those components; it is not allowed to access the .z component of a vec2 for example. Nosotros tin also pass vectors every bit arguments to different vector constructor calls, reducing the number of arguments required:

                      vec2 vect = vec2(0.5, 0.7); vec4 issue = vec4(vect, 0.0, 0.0); vec4 otherResult = vec4(result.xyz, 1.0);                  

Vectors are thus a flexible datatype that nosotros can employ for all kinds of input and output. Throughout the book y'all'll run across plenty of examples of how we can creatively manage vectors.

Ins and outs

Shaders are dainty little programs on their own, only they are part of a whole and for that reason nosotros want to have inputs and outputs on the private shaders so that we can move stuff around. GLSL defined the in and out keywords specifically for that purpose. Each shader can specify inputs and outputs using those keywords and wherever an output variable matches with an input variable of the adjacent shader stage they're passed along. The vertex and fragment shader differ a bit though.

The vertex shader should receive some class of input otherwise it would be pretty ineffective. The vertex shader differs in its input, in that it receives its input direct from the vertex data. To define how the vertex information is organized we specify the input variables with location metadata and then we tin can configure the vertex attributes on the CPU. We've seen this in the previous chapter as layout (location = 0). The vertex shader thus requires an extra layout specification for its inputs and then we tin link it with the vertex data.

It is also possible to omit the layout (location = 0) specifier and query for the attribute locations in your OpenGL code via glGetAttribLocation , merely I'd prefer to ready them in the vertex shader. It is easier to sympathize and saves you (and OpenGL) some piece of work.

The other exception is that the fragment shader requires a vec4 color output variable, since the fragment shaders needs to generate a last output color. If yous fail to specify an output color in your fragment shader, the color buffer output for those fragments will be undefined (which usually means OpenGL will render them either blackness or white).

And so if nosotros desire to transport information from one shader to the other we'd accept to declare an output in the sending shader and a similar input in the receiving shader. When the types and the names are equal on both sides OpenGL will link those variables together and so it is possible to transport data between shaders (this is done when linking a program object). To testify you lot how this works in practise we're going to alter the shaders from the previous affiliate to let the vertex shader decide the color for the fragment shader.

Vertex shader

                      #version 330 core layout (location = 0) in vec3 aPos; // the position variable has attribute position 0    out vec4 vertexColor; // specify a color output to the fragment shader  void main() {     gl_Position = vec4(aPos, one.0); // see how we directly give a vec3 to vec4's constructor     vertexColor = vec4(0.five, 0.0, 0.0, one.0); // set the output variable to a dark-scarlet color }                  

Fragment shader

                      #version 330 core out vec4 FragColor;    in vec4 vertexColor; // the input variable from the vertex shader (same name and aforementioned type)    void main() {     FragColor = vertexColor; }                  

Yous can run into we declared a vertexColor variable as a vec4 output that nosotros set in the vertex shader and we declare a similar vertexColor input in the fragment shader. Since they both have the aforementioned blazon and name, the vertexColor in the fragment shader is linked to the vertexColor in the vertex shader. Because nosotros set up the color to a dark-red color in the vertex shader, the resulting fragments should be dark-red too. The following epitome shows the output:

There we go! Nosotros just managed to send a value from the vertex shader to the fragment shader. Permit's spice information technology up a bit and see if we can transport a color from our application to the fragment shader!

Uniforms

Uniforms are another way to laissez passer data from our application on the CPU to the shaders on the GPU. Uniforms are however slightly unlike compared to vertex attributes. First of all, uniforms are global. Global, meaning that a uniform variable is unique per shader plan object, and tin can be accessed from any shader at any stage in the shader program. Second, whatever you ready the uniform value to, uniforms will go along their values until they're either reset or updated.

To declare a compatible in GLSL we only add the uniform keyword to a shader with a blazon and a name. From that betoken on nosotros tin can use the newly declared compatible in the shader. Allow's see if this time nosotros can set the color of the triangle via a uniform:

                      #version 330 cadre out vec4 FragColor;    compatible vec4 ourColor; // we fix this variable in the OpenGL code.  void main() {     FragColor = ourColor; }                  

Nosotros declared a uniform vec4 ourColor in the fragment shader and fix the fragment'south output colour to the content of this uniform value. Since uniforms are global variables, we can ascertain them in any shader phase we'd like and then no need to go through the vertex shader over again to go something to the fragment shader. Nosotros're not using this compatible in the vertex shader so there's no need to define information technology there.

If you declare a uniform that isn't used anywhere in your GLSL code the compiler will silently remove the variable from the compiled version which is the cause for several frustrating errors; keep this in listen!

The uniform is currently empty; we haven't added whatever data to the uniform yet so let'southward try that. We offset need to find the index/location of the compatible attribute in our shader. One time we have the index/location of the uniform, we can update its values. Instead of passing a single color to the fragment shader, let's spice things upwards by gradually changing colour over fourth dimension:

                      float timeValue =            glfwGetTime(); float greenValue = (sin(timeValue) / two.0f) + 0.5f; int vertexColorLocation =            glGetUniformLocation(shaderProgram, "ourColor");            glUseProgram(shaderProgram);            glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);                  

First, we retrieve the running fourth dimension in seconds via glfwGetTime(). Then we vary the color in the range of 0.0 - 1.0 by using the sin role and shop the upshot in greenValue.

So we query for the location of the ourColor uniform using glGetUniformLocation . We supply the shader program and the name of the compatible (that nosotros desire to retrieve the location from) to the query office. If glGetUniformLocation returns -1, information technology could non find the location. Lastly we can prepare the uniform value using the glUniform4f function. Note that finding the uniform location does not require y'all to use the shader program commencement, but updating a compatible does require yous to get-go use the program (by calling glUseProgram ), because it sets the uniform on the currently active shader program.

Because OpenGL is in its core a C library it does non take native support for part overloading, so wherever a function can exist called with different types OpenGL defines new functions for each type required; glUniform is a perfect example of this. The function requires a specific postfix for the blazon of the uniform y'all want to set. A few of the possible postfixes are:

  • f: the office expects a float as its value.
  • i: the function expects an int as its value.
  • ui: the function expects an unsigned int equally its value.
  • 3f: the function expects 3 bladdersouthward as its value.
  • fv: the function expects a float vector/array equally its value.
Whenever you want to configure an option of OpenGL just pick the overloaded function that corresponds with your type. In our case we desire to set four floats of the compatible individually and so we pass our information via glUniform4f (note that we too could've used the fv version).

Now that we know how to fix the values of uniform variables, we can apply them for rendering. If nosotros want the color to gradually alter, nosotros want to update this uniform every frame, otherwise the triangle would maintain a single solid colour if nosotros only set information technology once. And so we summate the greenValue and update the uniform each return iteration:

                      while(!glfwWindowShouldClose(window)) {     // input     processInput(window);      // return     // clear the colorbuffer                          glClearColor(0.2f, 0.3f, 0.3f, 1.0f);            glClear(GL_COLOR_BUFFER_BIT);      // be sure to actuate the shader            glUseProgram(shaderProgram);        // update the compatible color     bladder timeValue =            glfwGetTime();     bladder greenValue = sin(timeValue) / ii.0f + 0.5f;     int vertexColorLocation =            glGetUniformLocation(shaderProgram, "ourColor");            glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);      // now render the triangle            glBindVertexArray(VAO);            glDrawArrays(GL_TRIANGLES, 0, 3);        // bandy buffers and poll IO events            glfwSwapBuffers(window);            glfwPollEvents(); }                  

The lawmaking is a relatively straightforward adaptation of the previous lawmaking. This time, we update a uniform value each frame before drawing the triangle. If y'all update the compatible correctly you should see the color of your triangle gradually alter from green to blackness and back to green.

Check out the source code here if you lot're stuck.

As you can see, uniforms are a useful tool for setting attributes that may modify every frame, or for interchanging data betwixt your application and your shaders, but what if we desire to set a colour for each vertex? In that case we'd take to declare as many uniforms as we take vertices. A better solution would be to include more data in the vertex attributes which is what nosotros're going to practise at present.

More attributes!

We saw in the previous chapter how nosotros tin make full a VBO, configure vertex aspect pointers and store it all in a VAO. This time, we also want to add color data to the vertex data. We're going to add color data as 3 floats to the vertices array. Nosotros assign a blood-red, green and blue color to each of the corners of our triangle respectively:

                      bladder vertices[] = {     // positions         // colors      0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // lesser right     -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // bottom left      0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // top  };                  

Since we at present accept more data to send to the vertex shader, information technology is necessary to conform the vertex shader to too receive our color value as a vertex aspect input. Notation that nosotros set the location of the aColor attribute to 1 with the layout specifier:

                      #version 330 core layout (location = 0) in vec3 aPos;   // the position variable has aspect position 0 layout (location = 1) in vec3 aColor; // the color variable has attribute position 1    out vec3 ourColor; // output a color to the fragment shader  void principal() {     gl_Position = vec4(aPos, 1.0);     ourColor = aColor; // set ourColor to the input color nosotros got from the vertex information }                  

Since we no longer use a uniform for the fragment's color, but at present use the ourColor output variable we'll have to change the fragment shader likewise:

                      #version 330 cadre out vec4 FragColor;   in vec3 ourColor;    void primary() {     FragColor = vec4(ourColor, ane.0); }                  

Because nosotros added another vertex attribute and updated the VBO's retention we have to re-configure the vertex attribute pointers. The updated data in the VBO'due south memory at present looks a bit like this:

Interleaved data of position and color within VBO to be configured wtih <function id='30'>glVertexAttribPointer</function>

Knowing the current layout we can update the vertex format with glVertexAttribPointer :

                      // position attribute            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(bladder), (void*)0);                          glEnableVertexAttribArray(0); // colour attribute            glVertexAttribPointer(1, iii, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));                          glEnableVertexAttribArray(1);                  

The first few arguments of glVertexAttribPointer are relatively straightforward. This fourth dimension nosotros are configuring the vertex attribute on aspect location 1. The color values accept a size of iii floatdue south and we do non normalize the values.

Since we now have two vertex attributes nosotros accept to re-calculate the stride value. To get the next attribute value (e.g. the side by side 10 component of the position vector) in the information array we take to move 6 floats to the right, three for the position values and iii for the color values. This gives u.s. a stride value of six times the size of a float in bytes (= 24 bytes).
Too, this fourth dimension we have to specify an offset. For each vertex, the position vertex aspect is first and so we declare an offset of 0. The colour attribute starts after the position data and then the starting time is three * sizeof(float) in bytes (= 12 bytes).

Running the application should result in the following prototype:

Check out the source code here if you're stuck.

The image may not exist exactly what you would expect, since we merely supplied iii colors, not the huge color palette nosotros're seeing right now. This is all the result of something called fragment interpolation in the fragment shader. When rendering a triangle the rasterization phase usually results in a lot more fragments than vertices originally specified. The rasterizer then determines the positions of each of those fragments based on where they reside on the triangle shape.
Based on these positions, it interpolates all the fragment shader's input variables. Say for example we have a line where the upper point has a dark-green color and the lower point a blueish color. If the fragment shader is run at a fragment that resides effectually a position at 70% of the line, its resulting color input attribute would and then be a linear combination of green and blue; to be more precise: 30% blue and 70% green.

This is exactly what happened at the triangle. We have 3 vertices and thus 3 colors, and judging from the triangle'south pixels it probably contains effectually 50000 fragments, where the fragment shader interpolated the colors among those pixels. If yous take a practiced look at the colors you'll encounter it all makes sense: carmine to blue offset gets to purple and so to blue. Fragment interpolation is applied to all the fragment shader'due south input attributes.

Our ain shader class

Writing, compiling and managing shaders can exist quite cumbersome. As a final bear upon the shader field of study nosotros're going to make our life a chip easier by building a shader class that reads shaders from disk, compiles and links them, checks for errors and is piece of cake to apply. This also gives y'all a scrap of an idea how we can encapsulate some of the knowledge we learned so far into useful abstract objects.

Nosotros will create the shader form entirely in a header file, mainly for learning purposes and portability. Let'due south showtime by adding the required includes and by defining the class construction:

                      #ifndef SHADER_H #ascertain SHADER_H  #include <glad/glad.h> // include glad to get all the required OpenGL headers    #include <string> #include <fstream> #include <sstream> #include <iostream>     class Shader { public:     // the program ID     unsigned int ID;        // constructor reads and builds the shader     Shader(const char* vertexPath, const char* fragmentPath);     // employ/activate the shader     void use();     // utility compatible functions     void setBool(const std::string &name, bool value) const;       void setInt(const std::string &name, int value) const;        void setFloat(const std::string &proper noun, float value) const; };    

0 Response to "Unity Glsl Link Error: Warning: Output of Vertex Shader 'vs_texcoord0' Not Read by Fragment Shader"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel