Example Code: Difference between revisions
(→GLSL error testing: Added an example of OpenGL error testing code.) |
(→Texture: Added example of texture binding.) |
||
Line 3: | Line 3: | ||
== Texture == | == Texture == | ||
{{snippet|:Example/Texture Array Creation}} | {{snippet|:Example/Texture Array Creation}} | ||
{{snippet|:Example/Texture Shader Binding}} | |||
== OpenGL error testing == | == OpenGL error testing == |
Revision as of 14:41, 22 September 2017
This is a list of all of the significant example code used throughout the wiki.
Texture
This example code shows how to create a 2D array texture.
GLuint texture = 0;
GLsizei width = 2;
GLsizei height = 2;
GLsizei layerCount = 2;
GLsizei mipLevelCount = 1;
// Read you texels here. In the current example, we have 2*2*2 = 8 texels, with each texel being 4 GLubytes.
GLubyte texels[32] =
{
// Texels for first image.
0, 0, 0, 255,
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
// Texels for second image.
255, 255, 255, 255,
255, 255, 0, 255,
0, 255, 255, 255,
255, 0, 255, 255,
};
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D_ARRAY,texture);
// Allocate the storage.
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount);
// Upload pixel data.
// The first 0 refers to the mipmap level (level 0, since there's only 1)
// The following 2 zeroes refers to the x and y offsets in case you only want to specify a subrectangle.
// The final 0 refers to the layer index offset (we start from index 0 and have 2 levels).
// Altogether you can specify a 3D box subset of the overall texture, but only one mip level at a time.
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels);
// Always set reasonable texture parameters
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
This example shows how to associate textures with shader variables when rendering.
// Initial program setup.
glLinkProgram(program); // Initial link
GLint baseImageLoc = glGetUniformLocation(program, "baseImage");
GLint normalMapLoc = glGetUniformLocation(program, "normalMap");
GLint shadowMapLoc = glGetUniformLocation(program, "shadowMap");
glUseProgram(program);
glUniform1i(baseImageLoc, 0); // Texture unit 0 is for base images.
glUniform1i(normalMapLoc, 2); // Texture unit 2 is for normal maps.
glUniform1i(shadowMapLoc, 4); // Texture unit 4 is for shadow maps.
// When rendering an object with this program.
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, object1BaseImage);
glBindSampler(0, linearFiltering);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, object1NormalMap);
glBindSampler(2, linearFiltering); // Same filtering as before
glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, shadowMap);
glBindSampler(4, depthComparison); // Special sampler for depth comparisons.
// Render stuff
glDraw*();
// Render another object with some different textures.
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, object2BaseImage); // Use the same sampler as before.
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, object2NormalMap); // Use the same sampler as before.
// Use the same shadow map, so no need to unbind/bind.
// Render stuff
glDraw*();
OpenGL error testing
A simple loop to extract the current OpenGL errors:
GLenum err;
while((err = glGetError()) != GL_NO_ERROR)
{
// Process/log the error.
}
GLSL error testing
Shader compilation error checking.
GLuint shader = glCreateShader(...);
// Get strings for glShaderSource.
glShaderSource(shader, ...);
glCompileShader(shader);
GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
// Provide the infolog in whatever manor you deem best.
// Exit with failure.
glDeleteShader(shader); // Don't leak the shader.
return;
}
// Shader compilation is successful.
Program Linking error checking.
GLuint program = glCreateProgram();
// Attach shaders as necessary.
glAttachShader(program, ...);
...
// Link the program.
glLinkProgram(program);
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
// The program is useless now. So delete it.
glDeleteProgram(program);
// Provide the infolog in whatever manner you deem best.
// Exit with failure.
return;
}
GLSL shader compilation examples
Full compile/link of a Vertex and Fragment Shader.
// Read our shaders into the appropriate buffers
std::string vertexSource = // Get source code for vertex shader.
std::string fragmentSource = // Get source code for fragment shader.
// Create an empty vertex shader handle
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// Send the vertex shader source code to GL
// Note that std::string's .c_str is NULL character terminated.
const GLchar *source = (const GLchar *)vertexSource.c_str();
glShaderSource(vertexShader, 1, &source, 0);
// Compile the vertex shader
glCompileShader(vertexShader);
GLint isCompiled = 0;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(vertexShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Create an empty fragment shader handle
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Send the fragment shader source code to GL
// Note that std::string's .c_str is NULL character terminated.
source = (const GLchar *)fragmentSource.c_str();
glShaderSource(fragmentShader, 1, &source, 0);
// Compile the fragment shader
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(fragmentShader);
// Either of them. Don't leak shaders.
glDeleteShader(vertexShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Vertex and fragment shaders are successfully compiled.
// Now time to link them together into a program.
// Get a program object.
GLuint program = glCreateProgram();
// Attach our shaders to our program
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
// Link our program
glLinkProgram(program);
// Note the different functions here: glGetProgram* instead of glGetShader*.
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
// We don't need the program anymore.
glDeleteProgram(program);
// Don't leak shaders either.
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Always detach shaders after a successful link.
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
Creating two separable programs, one with a vertex shader and one with a fragment shader.
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
GLuint vertProg = glCreateShaderProgramv(GL_VERTEX_SHADER , 1, &vertSrc);
GLuint fragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragSrc);
// CHECK FOR ERRORS HERE!.
// Generate a program pipeline and attach the programs to their respective stages
glGenProgramPipelines(1, &pipeline);
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT , vertProg);
glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, fragProg);
// Query and set any uniforms
GLint colorLoc = glGetUniformLocation(fragProg, "Color");
glProgramUniform4f(fragProg, colorLoc, 1.f, 0.f, 0.f, 1.f);
Creates a separate program, where some of the stages are directly linked together.
// Create two programs. One with just the vertex shader, and
// one with both geometry and fragment stages.
GLuint vertexProgram = glCreateProgram();
GLuint geomFragProgram = glCreateProgram();
// Declare that programs are separable - this is crucial!
glProgramParameteri(vertexProgram , GL_PROGRAM_SEPARABLE, GL_TRUE);
glProgramParameteri(geomFragProgram, GL_PROGRAM_SEPARABLE, GL_TRUE);
// Generate and compile shader objects, as normal.
GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
GLuint geomShader = glCreateShader(GL_GEOMETRY_SHADER);
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertShader, 1, &vertSrc, NULL);
glShaderSource(geomShader, 1, &geomSrc, NULL);
glShaderSource(fragShader, 1, &fragSrc, NULL);
glCompileShader(vertShader);
glCompileShader(geomShader);
glCompileShader(fragShader);
// Attach the shaders to their respective programs
glAttachShader(vertexProgram , vertShader);
glAttachShader(geomFragProgram, geomShader);
glAttachShader(geomFragProgram, fragShader);
// Perform any pre-linking steps.
glBindAttribLocation(vertexProgram , 0, "Position");
glBindFragDataLocation(geomFragProgram, 0, "FragColor");
// Link the programs
glLinkProgram(vertexProgram);
glLinkProgram(geomFragProgram);
// Detach and delete the shader objects
glDetachShader(vertexProgram, vertShader);
glDeleteShader(vertShader);
glDetachShader(geomFragProgram, geomShader);
glDetachShader(geomFragProgram, fragShader);
glDeleteShader(geomShader);
glDeleteShader(fragShader);
// Generate a program pipeline
glGenProgramPipelines(1, &pipeline);
// Attach the first program to the vertex stage, and the second program
// to the geometry and fragment stages
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, vertexProgram);
glUseProgramStages(pipeline, GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, geomFragProgram);
Full compile/link through SPIR-V of a Vertex and Fragment Shader.
// Read our shaders into the appropriate buffers
std::vector<unsigned char> vertexSpirv = // Get SPIR-V for vertex shader.
std::vector<unsigned char> fragmentSpirv = // Get SPIR-V for fragment shader.
// Create an empty vertex shader handle
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// Apply the vertex shader SPIR-V to the shader object.
glShaderBinary(1, &vertexShader, GL_SHADER_BINARY_FORMAT_SPIR_V, vertexSpirv.data(), vertexSpirv.size());
// Specialize the vertex shader.
std::string vsEntrypoint = ...; // Get VS entry point name
glSpecializeShader(vertexShader, (const GLchar*)vsEntrypoint.c_str(), 0, nullptr, nullptr);
// Specialization is equivalent to compilation.
GLint isCompiled = 0;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(vertexShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Create an empty fragment shader handle
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Apply the fragment shader SPIR-V to the shader object.
glShaderBinary(1, &fragmentShader, GL_SHADER_BINARY_FORMAT_SPIR_V, fragmentSpirv.data(), fragmentSpirv.size());
// Specialize the fragment shader.
std::string fsEntrypoint = ...; //Get VS entry point name
glSpecializeShader(fragmentShader, (const GLchar*)fsEntrypoint.c_str(), 0, nullptr, nullptr);
// Specialization is equivalent to compilation.
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(fragmentShader);
// Either of them. Don't leak shaders.
glDeleteShader(vertexShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Vertex and fragment shaders are successfully compiled.
// Now time to link them together into a program.
// Get a program object.
GLuint program = glCreateProgram();
// Attach our shaders to our program
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
// Link our program
glLinkProgram(program);
// Note the different functions here: glGetProgram* instead of glGetShader*.
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
// We don't need the program anymore.
glDeleteProgram(program);
// Don't leak shaders either.
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// Use the infoLog as you see fit.
// In this simple program, we'll just leave
return;
}
// Always detach shaders after a successful link.
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
GLSL uniform location usage examples
Querying and using uniform locations, under the new introspection API.
Vertex shader code:
...
uniform vec3 someVector;
uniform mat4 someMatrix;
...
// Code must use these uniforms to be active.
C++ code:
GLuint program = // Compile and link program.
GLint someVectorLoc = glGetProgramResourceLocation(program, GL_UNIFORM, "someVector");
assert(someVectorLoc != -1); // If fails, then the uniform is inactive.
GLint someMatrixLoc = glGetProgramResourceLocation(program, GL_UNIFORM, "someMatrix");
assert(someMatrixLoc != -1); // If fails, then the uniform is inactive.
...
glProgramUniform3f(program, someVectorLoc, 3.0f, 3.0f, 4.0f);
GLfloat matrix[16];
// Fill `matrix` with data. Non-transposed matrix data.
glProgramUniformMatrix4fv(program, someMatrixLoc, 1, GL_FALSE, matrix);
Querying and using uniform locations, under the old introspection API.
Vertex shader code:
...
uniform vec3 someVector;
uniform mat4 someMatrix;
...
// Code must use these uniforms to be active.
C++ code:
GLuint program = // Compile and link program.
GLint someVectorLoc = glGetUniformLocation(program, "someVector");
assert(someVectorLoc != -1); // If fails, then the uniform is inactive.
GLint someMatrixLoc = glGetUniformLocation(program, "someMatrix");
assert(someMatrixLoc != -1); // If fails, then the uniform is inactive.
...
glUseProgram(program);
glUniform3f(someVectorLoc, 3.0f, 3.0f, 4.0f);
GLfloat matrix[16];
// Fill `matrix` with data. Non-transposed matrix data.
glUniformMatrix4fv(someMatrixLoc, 1, GL_FALSE, matrix);
GLSL expression examples
This example code shows what are and are not dynamically uniform expressions.
in vec3 fromPrevious;
in uvec2 fromRange;
const int foo = 5;
const uvec2 range = uvec2(2, 5);
uniform vec2 pairs;
uniform sampler2d tex;
void main()
{
foo; // constant expressions are dynamically uniform.
uint value = 21; // 'value' is dynamically uniform.
value = range.x; // still dynamically uniform.
value = range.y + fromRange.y; // not dynamically uniform; current contents come from a non-dynamically uniform source.
value = 4; // dynamically uniform again.
if (fromPrevious.y < 3.14)
value = 12;
value; // NOT dynamically uniform. Current contents depend on 'fromPrevious', an input variable.
float number = abs(pairs.x); // 'number' is dynamically uniform.
number = sin(pairs.y); // still dynamically uniform.
number = cos(fromPrevious.x); // not dynamically uniform.
vec4 colors = texture(tex, pairs.xy); // dynamically uniform, even though it comes from a texture.
// It uses the same texture coordinate, thus getting the same texel every time.
colors = texture(tex, fromPrevious.xy); // not dynamically uniform.
for(int i = range.x; i < range.y; ++i)
{
// loop initialized with, compared against, and incremented by dynamically uniform expressions.
i; // Therefore, 'i' is dynamically uniform, even though it changes.
}
for(int i = fromRange.x; i < fromRange.y; ++i)
{
i; // 'i' is not dynamically uniform; 'fromRange' is not dynamically uniform.
}
}
Debug Output
This example code shows how to get the first X messages from the debug output log.
void GetFirstNMessages(GLuint numMsgs)
{
GLint maxMsgLen = 0;
glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &maxMsgLen);
std::vector<GLchar> msgData(numMsgs * maxMsgLen);
std::vector<GLenum> sources(numMsgs);
std::vector<GLenum> types(numMsgs);
std::vector<GLenum> severities(numMsgs);
std::vector<GLuint> ids(numMsgs);
std::vector<GLsizei> lengths(numMsgs);
GLuint numFound = glGetDebugMessageLog(numMsgs, msgs.size(), &sources[0], &types[0], &ids[0], &severities[0], &lengths[0], &msgData[0]);
sources.resize(numFound);
types.resize(numFound);
severities.resize(numFound);
ids.resize(numFound);
lengths.resize(numFound);
std::vector<std::string> messages;
messages.reserve(numFound);
std::vector<GLchar>::iterator currPos = msgData.begin();
for(size_t msg = 0; msg < lengths.size(); ++msg)
{
messages.push_back(std::string(currPos, currPos + lengths[msg] - 1));
currPos = currPos + lengths[msg];
}
}
Program introspection
Iteration over all non-block uniform variables, fetching their names, types, and locations:
GLint numUniforms = 0;
glGetProgramInterfaceiv(prog, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms);
const GLenum properties[4] = {GL_BLOCK_INDEX, GL_TYPE, GL_NAME_LENGTH, GL_LOCATION};
for(int unif = 0; unif < numUniforms; ++unif)
{
GLint values[4];
glGetProgramResourceiv(prog, GL_UNIFORM, unif, 4, properties, 4, NULL, values);
// Skip any uniforms that are in a block.
if(values[0] != -1)
continue;
// Get the name. Must use a std::vector rather than a std::string for C++03 standards issues.
// C++11 would let you use a std::string directly.
std::vector<char> nameData(values[2]);
glGetProgramResourceName(prog, GL_UNIFORM, unif, nameData.size(), NULL, &nameData[0]);
std::string name(nameData.begin(), nameData.end() - 1);
}
Iteration over all uniforms within each uniform block.
GLint numBlocks = 0;
glGetProgramInterfaceiv(prog, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &numBlocks);
const GLenum blockProperties[1] = {GL_NUM_ACTIVE_VARIABLES};
const GLenum activeUnifProp[1] = {GL_ACTIVE_VARIABLES};
const GLenum unifProperties[3] = {GL_NAME_LENGTH, GL_TYPE, GL_LOCATION};
for(int blockIx = 0; blockIx < numBlocks; ++blockIx)
{
GLint numActiveUnifs = 0;
glGetProgramResourceiv(prog, GL_UNIFORM_BLOCK, blockIx, 1, blockProperties, 1, NULL, &numActiveUnifs);
if(!numActiveUnifs)
continue;
std::vector<GLint> blockUnifs(numActiveUnifs);
glGetProgramResourceiv(prog, GL_UNIFORM_BLOCK, blockIx, 1, activeUnifProp, numActiveUnifs, NULL, &blockUnifs[0]);
for(int unifIx = 0; unifIx < numActiveUnifs; ++unifIx)
{
GLint values[3];
glGetProgramResourceiv(prog, GL_UNIFORM, blockUnifs[unifIx], 3, unifProperties, 3, NULL, values);
// Get the name. Must use a std::vector rather than a std::string for C++03 standards issues.
// C++11 would let you use a std::string directly.
std::vector<char> nameData(values[0]);
glGetProgramResourceName(prog, GL_UNIFORM, blockUnifs[unifIx], nameData.size(), NULL, &nameData[0]);
std::string name(nameData.begin(), nameData.end() - 1);
}
}