GLSL – How to Set up the Shaders from the Host Application Side

Exclusive offer: get 50% off this eBook here
GLSL Essentials

GLSL Essentials — Save 50%

Enrich your 3D scenes with the power of GLSL! with this book and ebook

$20.99    $10.50
by Jacobo Rodríguez | December 2013 | Open Source

In this article written by Jacobo Rodríguez, the author of the book GLSL Essentials, we will learn how to set up the shaders from the host application side.

OpenGL 4.3 is a C language API that bases its design in encapsulating objects in opaque handles that represents abstract concepts (from the user's point of view) such as textures, shaders, vertex buffers, and so on. In order to render something using OpenGL, we have to create those objects, associate our data to them, and issue the required OpenGL commands to set them as active, and in the last term, launch the draw call.

Let's define an important computer graphics concept: a rendering batch. A rendering batch is the geometry set that will be rendered along with the textures, OpenGL's states and shaders. Once we have all that data ready, we can issue the drawing command to the GPU, and hopefully (if we did everything correctly) watch the rendering in our screen.

The order of the creation of the different OpenGL objects is not relevant. You can first create the vertex buffer or the shaders, or first the textures and then the shaders. I will use the following order just for teaching purposes:

  1. Vertex array objects
  2. Textures
  3. Shaders

Then, I will put all together and render the batch.

(For more resources related to this topic, see here.)

Setting up geometry

Let's say that our mesh (a quad formed by two triangles) has the following information: vertex positions and texture coordinates. Also, we will arrange the data interleaved in the array.

struct MyVertex { float x, y, z; float s, t; }; MyVertex geometry[] =  {{0,1,0,0,1}, {0,0,0,0,0}, {1,1,0,1,1},

{1,0,0,1,0}}; // Let's create the objects that will encapsulate our geometry data GLuint vaoID, vboID; glGenVertexArray(1, &vaoID); glBindVertexArray(vaoID); glGenBuffers(1, &vboID); glBindBuffer(GL_ARRAY_BUFFER, vboID); // Attach our data to the OpenGL objects glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(MyVertex), &geometry[0].x,

GL_DYNAMIC_DRAW); // Specify the format of each vertex attribute glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MyVertex), NULL); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(MyVertex),

(void*)(sizeof(float)*3));

At this point, we created the OpenGL objects, set up correctly each vertex attribute format and uploaded the data to GPU.

Setting up textures

Setting up textures follows the same pattern. First create the OpenGL objects, then fill the data and the format in which it is provided.

const int width = 512; const int height = 512; const int bpp = 32; struct RGBColor { unsigned char R,G,B,A; }; RGBColor textureData[width * height]; for(size_t y = 0; y < height; ++y) for(size_t x = 0; x < width; ++x)                textureData[y*height+x] = …; //fill your texture here // Create GL object GLuint texID; glGenTextures(1, &texID); glBindTexture(GL_TEXTURE_2D, texID); // Fill up the data and set the texel format glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,

GL_UNSIGNED_BYTE, &textureData[0].R); // Set texture format data: interpolation and clamping modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

And that's all about textures. Later we will use the texture ID to place the texture into a slot, and that slot number is the information that we will pass to the shader to tell it where the texture is placed in order to locate it.

Setting up shaders

In order to setup the shaders, we have to carry out some steps:  load up the source code, compile it and associate to a shader object, and link all shaders together into a program object.

char* vs[1]; vs[0] = "#version 430\nlayout (location = 0) in vec3 PosIn;

layout (location = 1) in vec2 TexCoordIn;

smooth out vec2 TexCoordOut;

uniform mat4 MVP; void main() { TexCoordOut = TexCoordIn;

gl_Position = MVP * vec4(PosIn, 1.0);}"; char fs[1]; fs = "#version 430\n uniform sampler2D Image; smooth in vec2 TexCoord;

out vec4 FBColor;

void main() {FBColor = texture(Image, TexCoord);}"; // Upload source code and compile it GLuint pId, vsId, fsId; vsId = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vsId, 1, (const char**)&vs, NULL); glCompileShader(vsId); // Check for compilation errors GLint status = 0, bufferLength = 0; glGetShaderiv(vsId, GL_COMPILE_STATUS, &status); if(!status) { char* infolog = new char[bufferLength + 1]; glGetShaderiv(vsId, GL_INFO_LOG_LENGTH, &bufferLength); glGetShaderInfoLog(vsId, bufferLength, NULL, infolog); infolog[bufferLength] = 0; printf("Shader compile errors / warnings: %s\n", infolog); delete [] infolog; }

The process for the fragment shader is exactly the same. The only change is that the shader object must be created as fsId = glCreateShader(GL_FRAGMENT_SHADER);

// Now let's proceed to link the shaders into the program object pId = glCreateProgram(); glAttachShader(pId, vsId); glAttachShader(pId, fsId); glLinkProgram(pId); glGetProgramiv(pId, GL_LINK_STATUS, &status); if(!status) { char* infolog = new char[bufferLength + 1]; glGetProgramiv(pId, GL_INFO_LOG_LENGTH, &bufferLength); infolog[bufferLength] = 0; printf("Shader linking errors / warnings: %s\n", infolog); delete [] infolog; } // We do not need the vs and fs anymore, so it is same mark them for deletion. glDeleteShader(vsId); glDeleteShader(fsId);

The last things to upload to the shader are two uniform variables: the one that corresponds with the view-projection matrix and the one that represents the texture. Those are uniform variables and are set in the following way:

// First bind the program object where we want to upload the variables glUseProgram(pId); // Obtain the "slot number" where the uniform is located in GLint location = glGetUniformLocation(pId, "MVP"); float mvp[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; // Set the data into the uniform's location glUniformMatrix4fv(location, 1, GL_FALSE, mvp); // Active the texture slot 0 glActiveTexture(GL_TEXTURE0); // Bind the texture to the active slot glBindTexture(GL_TEXTURE_2D, texID); location = glGetUniformLocation(pId, "Image"); // Upload the texture's slot number to the uniform variable int imageSlot = 0; glUniform1i(location, imageSlot);

And that's all. For the other types of shaders, process is all the same: Create shader object, upload source code, compile and link, but using the proper OpenGL types such as GL_GEOMETRY_SHADER or GL_COMPUTE_SHADER.

A last step, to draw all these things, is to establish them as active and issue the draw call:

glBindVertexArray(vaoID); glBindTexture(GL_TEXTURE_2D, texID); glUseProgram(pId); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

Resources for Article:


Further resources on this subject:


GLSL Essentials Enrich your 3D scenes with the power of GLSL! with this book and ebook
Published: December 2013
eBook Price: $20.99
Book Price: $34.99
See more
Select your format and quantity:

About the Author :


Jacobo Rodríguez

Jacobo Rodríguez is a real-time computer graphics programmer living in the north of Spain. He has working experience with computer graphics, digital photogrammetry, computer vision, and video game development. Jacobo has worked for cutting-edge technology companies such as Metria Digital and Blit Software, and has also worked as an entrepreneur and freelancer for a variety of clients of platforms such as PC, iOS, PlayStation 3, PlayStation Vita, and PlayStation Portable. Jacobo has been working and learning at the same time for the last 20 years in the computer graphics field in roles ranging from junior programmer to project manager, passing through R&D director as well. Jacobo has always been very committed to the computer graphics community, having released for free the OpenGL Shader Designer: the first application in the world (even before NVIDIA with FX Composer or ATI with RenderMonkey) designed to visually develop and program GLSL shaders, as well as some OpenGL programming tutorials, all forming part of the Official OpenGL SDK.

Books From Packt


OpenGL 4.0 Shading Language Cookbook
OpenGL 4.0 Shading Language Cookbook

Learning Game Physics with Bullet Physics and OpenGL
Learning Game Physics with Bullet Physics and OpenGL

Torque 3D Game Development Cookbook
Torque 3D Game Development Cookbook

OpenCL Programming by Example
OpenCL Programming by Example

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide

OpenGL 4 Shading Language Cookbook, Second Edition
OpenGL 4 Shading Language Cookbook, Second Edition

3D Game Development with Microsoft Silverlight 3: Beginner's Guide
3D Game Development with Microsoft Silverlight 3: Beginner's Guide

OpenGL Development Cookbook
OpenGL Development Cookbook


Your rating: None Average: 1 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
u
M
P
F
B
X
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software