As mentioned in the Hello Triangle chapter, shaders are little programs that rest on the GPU. These programs are run for each specific section of the graphics pipeline. In a basic sense, shaders are nothing more than programs transforming inputs to outputs. Shaders are also very isolated programs in that they're not allowed to communicate with each other; the only communication they have is via their inputs and outputs.
In the previous chapter we briefly touched the surface of shaders and how to properly use them. We will now explain shaders, and specifically the OpenGL Shading Language, in a more general fashion.
Our goal is for Newgrounds to be ad free for everyone! Become a Supporter today and help make this dream a reality! Download sample pack VEC2 sample pack nya enak banget gan sok mangga di cobian:D. Paswrd: musicrifk13.blogspot.co.id. Posted by musicrifk13 at. Vec2 Pack(const vector2& obj); vector2 UnPack( const Vec2& obj); nativetypepackname('name') (on a struct when nativetype is specified, too): when you want to use the same nativetype multiple times (e. With different precision) you must make the names of the Pack/UnPack functions unique, otherwise you will run into compile errors. For example, dot does not require pack/unpack because it is defined in terms of zipWith and fold. However transpose, det, gaussElim and most others are recursive (i.e., defined in terms of the same operation on lower-dimensional vectors), and so you'll still need to use pack/unpack with these.
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 begin with a version declaration, followed by a list of input and output variables, uniforms and its
A shader typically has the following structure:
When we're talking specifically about the vertex shader each input variable is also known as a
This often returns the minimum of 16
which should be more than enough for most purposes.
Types
GLSL has, like any other programming language, data types for specifying what kind of variable we want to work with. GLSL has most of the default basic types we know from languages like C: int
, float
, double
, uint
and bool
. GLSL also features two container types that we'll be using a lot, namely vectors
and matrices
. We'll discuss matrices in a later chapter.
Vectors
A vector in GLSL is a 1,2,3 or 4 component container for any of the basic types just mentioned. They can take the following form (n
represents the number of components):
vecn
: the default vector ofn
floats.bvecn
: a vector ofn
booleans.ivecn
: a vector ofn
integers.uvecn
: a vector ofn
unsigned integers.dvecn
: a vector ofn
double components.
Most of the time we will be using the basic vecn
since floats are sufficient for most of our purposes.
Components of a vector can be accessed via vec.x
where x
is the first component of the vector. You can use .x
, .y
, .z
and .w
to access their first, second, third and fourth component respectively. GLSL also allows you to use rgba
for colors or stpq
for texture coordinates, accessing the same components.
The vector datatype allows for some interesting and flexible component selection called
You can use any combination of up to 4 letters to create a new vector (of the same type) as long as the original vector has those components; it is not allowed to access the .z
component of a vec2
for example. We can also pass vectors as arguments to different vector constructor calls, reducing the number of arguments required:
Vectors are thus a flexible datatype that we can use for all kinds of input and output. Throughout the book you'll see plenty of examples of how we can creatively manage vectors.
Ins and outs
Shaders are nice little programs on their own, but they are part of a whole and for that reason we want to have inputs and outputs on the individual 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 next shader stage they're passed along. The vertex and fragment shader differ a bit though.
The vertex shader should receive some form of input otherwise it would be pretty ineffective. The vertex shader differs in its input, in that it receives its input straight from the vertex data. To define how the vertex data is organized we specify the input variables with location metadata so we 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 so we can link it with the vertex data.
layout (location = 0)
specifier and query for the attribute locations in your OpenGL code via The other exception is that the fragment shader requires a vec4
color output variable, since the fragment shaders needs to generate a final output color. If you 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 black or white).
So if we want to send data from one shader to the other we'd have 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 then it is possible to send data between shaders (this is done when linking a program object). To show you how this works in practice we're going to alter the shaders from the previous chapter to let the vertex shader decide the color for the fragment shader.
Vertex shaderFragment shader You can see we declared a vertexColor variable as a vec4
output that we set in the vertex shader and we declare a similar vertexColor input in the fragment shader. Since they both have the same type and name, the vertexColor in the fragment shader is linked to the vertexColor in the vertex shader. Because we set the color to a dark-red color in the vertex shader, the resulting fragments should be dark-red as well. The following image shows the output:
There we go! We just managed to send a value from the vertex shader to the fragment shader. Let's spice it up a bit and see if we can send a color from our application to the fragment shader!
Uniforms
To declare a uniform in GLSL we simply add the uniform
keyword to a shader with a type and a name. From that point on we can use the newly declared uniform in the shader. Let's see if this time we can set the color of the triangle via a uniform:
We declared a uniform vec4
ourColor in the fragment shader and set the fragment's output color to the content of this uniform value. Since uniforms are global variables, we can define them in any shader stage we'd like so no need to go through the vertex shader again to get something to the fragment shader. We're not using this uniform in the vertex shader so there's no need to define it there.
The uniform is currently empty; we haven't added any data to the uniform yet so let's try that. We first need to find the index/location of the uniform attribute in our shader. Once 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 up by gradually changing color over time:
First, we retrieve the running time in seconds via 0.0
- 1.0
by using the
Then we query for the location of the ourColor uniform using -1
, it could not find the location. Lastly we can set the uniform value using the
Because OpenGL is in its core a C library it does not have native support for function overloading, so wherever a function can be called with different types OpenGL defines new functions for each type required;
f
: the function expects afloat
as its value.i
: the function expects anint
as its value.ui
: the function expects anunsigned int
as its value.3f
: the function expects 3float
s as its value.fv
: the function expects afloat
vector/array as its value.
fv
version).Now that we know how to set the values of uniform variables, we can use them for rendering. If we want the color to gradually change, we want to update this uniform every frame, otherwise the triangle would maintain a single solid color if we only set it once. So we calculate the greenValue and update the uniform each render iteration:
The code is a relatively straightforward adaptation of the previous code. This time, we update a uniform value each frame before drawing the triangle. If you update the uniform correctly you should see the color of your triangle gradually change from green to black and back to green.
Check out the source code here if you're stuck.
As you can see, uniforms are a useful tool for setting attributes that may change every frame, or for interchanging data between your application and your shaders, but what if we want to set a color for each vertex? In that case we'd have to declare as many uniforms as we have vertices. A better solution would be to include more data in the vertex attributes which is what we're going to do now.
More attributes!
We saw in the previous chapter how we can fill a VBO, configure vertex attribute 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 float
s to the vertices array. We assign a red, green and blue color to each of the corners of our triangle respectively:
Since we now have more data to send to the vertex shader, it is necessary to adjust the vertex shader to also receive our color value as a vertex attribute input. Note that we set the location of the aColor attribute to 1 with the layout specifier:
Since we no longer use a uniform for the fragment's color, but now use the ourColor output variable we'll have to change the fragment shader as well:
Because we added another vertex attribute and updated the VBO's memory we have to re-configure the vertex attribute pointers. The updated data in the VBO's memory now looks a bit like this:
glVertexAttribPointer'/> Knowing the current layout we can update the vertex format with
The first few arguments of 1
. The color values have a size of 3
float
s and we do not normalize the values.
Since we now have two vertex attributes we have to re-calculate the stride value. To get the next attribute value (e.g. the next x
component of the position vector) in the data array we have to move 6
float
s to the right, three for the position values and three for the color values. This gives us a stride value of 6 times the size of a float
in bytes (= 24
bytes).
Also, this time we have to specify an offset. For each vertex, the position vertex attribute is first so we declare an offset of 0
. The color attribute starts after the position data so the offset is 3 * sizeof(float)
in bytes (= 12
bytes).
Running the application should result in the following image:
Check out the source code here if you're stuck.
The image may not be exactly what you would expect, since we only supplied 3 colors, not the huge color palette we're seeing right now. This is all the result of something called
Based on these positions, it 70%
of the line, its resulting color input attribute would 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's pixels it probably contains around 50000 fragments, where the fragment shader interpolated the colors among those pixels. If you take a good look at the colors you'll see it all makes sense: red to blue first gets to purple and then to blue. Fragment interpolation is applied to all the fragment shader's input attributes.
Writing, compiling and managing shaders can be quite cumbersome. As a final touch on the shader subject we're going to make our life a bit easier by building a shader class that reads shaders from disk, compiles and links them, checks for errors and is easy to use. This also gives you a bit of an idea how we can encapsulate some of the knowledge we learned so far into useful abstract objects.
We will create the shader class entirely in a header file, mainly for learning purposes and portability. Let's start by adding the required includes and by defining the class structure:
The shader class holds the ID of the shader program. Its constructor requires the file paths of the source code of the vertex and fragment shader respectively that we can store on disk as simple text files. To add a little extra we also add several utility functions to ease our lives a little:
Reading from file
We're using C++ filestreams to read the content from the file into several string
objects:
Next we need to compile and link the shaders. Note that we're also reviewing if compilation/linking failed and if so, print the compile-time errors. This is extremely useful when debugging (you are going to need those error logs eventually):
The
Similarly for any of the uniform setter functions:
And there we have it, a completed shader class. Using the shader class is fairly easy; we create a shader object once and from that point on simply start using it:
Vec2 Pack Download
Here we stored the vertex and fragment shader source code in two files called shader.vs
and shader.fs
. You're free to name your shader files however you like; I personally find the extensions .vs
and .fs
quite intuitive.
You can find the source code here using our newly created shader class. Note that you can click the shader file paths to find the shaders' source code.
Vec2 Sample Pack
- Adjust the vertex shader so that the triangle is upside down: solution.
- Specify a horizontal offset via a uniform and move the triangle to the right side of the screen in the vertex shader using this offset value: solution.
- Output the vertex position to the fragment shader using the
out
keyword and set the fragment's color equal to this vertex position (see how even the vertex position values are interpolated across the triangle). Once you managed to do this; try to answer the following question: why is the bottom-left side of our triangle black?: solution.
Texture2D
Retrieves texels from a texture
Declaration
Parameters
sampler
specifies the sampler to which the texture from which texels will be retrieved is bound.
coord
specifies the texture coordinates at which texture will be sampled.
Vec Package
bias
specifies an optional bias to be applied during level-of-detail computation.
Description
The texture2D function returns a texel, i.e. the (color) value of the texture for the given coordinates. The function has one input parameter of the type sampler2D and one input parameter of the type vec2
: sampler, the uniform the texture is bound to, and coord, the 2-dimensional coordinates of the texel to look up.
There is an optional third input parameter of the type float: bias. After calculating the appropriate level of detail for a texture with mipmaps the bias is added before the actual texture lookup operation is executed.
Side note: On iOS devices texture lookup functionality is only available in the fragment shader.