Today I’m going to show you another way to draw coloured triangles in Warp3D Nova. The result will be more boring than the previous tutorial’s method. However, it’s a good way to introduce using multiple vertex attributes and you’ll need that for almost everything from here on out.
Per Vertex Colour
Vertex attributes are essentially “attributes that describe each vertex.” The vertex’s position is a vertex attribute, but vertices can have other properties too. For example, in this tutorial each vertex will also have its own colour. We’ll make one vertex red, another green, and the third blue. The GPU will render smooth gradients between one vertex and the next (see the screenshot at the end of this tutorial).
Other typical attributes are texture coordinates and surface normals. However, let’s not get ahead of ourselves. For now we’ll stick to just position and colour.
Open the vertex shader (Colour2D.vert), and replace its code with the following:
#version 310 es
/** Simple 2D per-vertex colour shader.
*/
in layout(location = 0) vec2 vertPos;
in layout(location = 1) vec4 vertCol;
out vec4 colour;
void main() {
colour = vertCol;
gl_Position = vec4(vertPos, 0.0, 1.0);
}
This shader adds an extra input variable which is the vertex colour:
in layout(location = 1) vec4 vertCol;
You’ve probably noticed that both inputs have layout(location = n) qualifiers. This is a useful newish GLSL feature that allows us to fix the locations of the input variables. Warp3D Nova will sort the inputs in ascending location order.
Using layout(location = n) is optional. However, if you don’t use it, then the shader compiler can put the variables in any order, and you’ll need ShaderGetOffset() to find out which attribute indiex belongs to which shader variables. Life’s simpler when the locations are fixed.
Layout(location = n) is only available in relatively new versions of GLSL. So, a #version is required at the top of the file, or the compiler won’t accept it:
#version 310 es
The Fragment Shader
No changes are needed for the fragment shader. It already outputs the interpolated colour that it receives. Here’s the code:
With the shaders done, the next step is to set up the vertex attribute arrays. Our vertices now have two attributes which we’ll put in a structure:
/** Encapsulates the data for a single vertex
*/
typedef struct Vertex_s {
float position[2];
float colour[4];
} Vertex;
/** The index for the position data in the VBO (must match the Vertex structure).
*/
const uint32 posArrayIdx = 0;
/** The index for the colour data in the VBO (must match the Vertex structure).
*/
const uint32 colArrayIdx = 1;
The Vertex structure makes writing the vertices easier, while the *Idx variables will be used to tell Warp3D Nova where to find the position and colour arrays.
Now, go down to the main() function, and find the CreateVertexBufferObjectTags() call. We must allocate a Vertex Buffer Object (VBO) with two arrays to store both vertPos and vertCol:
// Create the Vertex Buffer Object (VBO) containing the triangle
uint32 numVerts = 3;
uint32 numArrays = 2; // Have a position and colour array
vbo = context->CreateVertexBufferObjectTags(&errCode,
numVerts * sizeof(Vertex), W3DN_STATIC_DRAW, numArrays, TAG_DONE);
FAIL_ON_ERROR(errCode, "CreateVertexBufferObjectTags");
Next, the VBO’s layout needs to be set. This lets Warp3D Nova know where to find the data for each vertex attribute. We’re using an “interleaved” format where each vertex’s data is stored together:
Pay close attention to the colour array’s offset. The offset is the bytes from the VBO’s base to the first vertex’s colour. Fortunately, C has the offsetof() macro which makes calculating this offset easy.
Next, the actual vertex data is written to the VBO. Adding the colour data results in the following code:
// Lock the VBO for access
// NOTE: We're replacing all data in the VBO, so the read range is 0
W3DN_BufferLock *vboLock = context->VBOLock(&errCode, vbo, 0, 0);
FAIL_ON_ERROR(errCode, "VBOLock");
// Vertex 0
// -- position
Vertex *vertices = (Vertex*)vboLock->buffer;
vertices[0].position[0] = 100.0f;
vertices[0].position[1] = 50.0f;
// -- Colour
vertices[0].colour[0] = 1.0f;
vertices[0].colour[1] = 0.0f;
vertices[0].colour[2] = 0.0f;
vertices[0].colour[3] = 1.0f;
// Vertex 1
// -- position
vertices[1].position[0] = 320.0f;
vertices[1].position[1] = 420.0f;
// -- Colour
vertices[1].colour[0] = 0.0f;
vertices[1].colour[1] = 1.0f;
vertices[1].colour[2] = 0.0f;
vertices[1].colour[3] = 1.0f;
// Vertex 2
// -- position
vertices[2].position[0] = 540.0f;
vertices[2].position[1] = 50.0f;
// -- Colour
vertices[2].colour[0] = 0.0f;
vertices[2].colour[1] = 0.0f;
vertices[2].colour[2] = 1.0f;
vertices[2].colour[3] = 1.0f;
// Unlock the VBO
// NOTE: We've updated the entire VBO, so the parameters reflect that
errCode = context->BufferUnlock(vboLock, 0, vboLock->size);
FAIL_ON_ERROR(errCode, "BufferUnlock");
The final change is to bind the two vertex attribute arrays to their respective shader input variables. Since the variable locations were explicitly set in the shader, this is relatively easy:
uint32 posAttribIdx = 0;
uint32 colAttribIdx = 1;
// Bind the VBO to the default Render State Object (RSO)
errCode = context->BindVertexAttribArray(NULL,
posAttribIdx, vbo, posArrayIdx);
FAIL_ON_ERROR(errCode, "BindVertexAttribArray");
errCode = context->BindVertexAttribArray(NULL,
colAttribIdx, vbo, colArrayIdx);
FAIL_ON_ERROR(errCode, "BindVertexAttribArray");
The code above could be shortened even further because the vertex array and attribute indices are identical (e.g., posArrayIdx == posAttribIdx). I’ve created separate variables for clarity.
IMPORTANT: Warp3D Nova version 1.20+ is required to use layout(location = n). The first Warp3D Nova pre-release didn’t include this feature. If you’re using the pre-release then delete layout(location = n) from the vertex shader, and set posAttribIdx and posAttribIdx as follows:
This is the old way of mapping shader variables to attributes and exists for compatibility with old shaders. It’s highly recommended that you fix variable locations using layout(location = n).
With these code changes done, the program now renders a coloured triangle (see below).
Conclusion
This tutorial has shown how to use multiple vertex attributes to add colour. Each vertex has its own colour and the GPU creates a smooth gradient between them via interpolation.
Yes, it looks rather boring, but make sure you understand how to use multiple vertex attributes; you’re going to need it a lot in future. Next time we’ll add surface normals (as vertex attributes) and finally render something in 3D!
We use technologies like cookies to store and/or access device information. Some are essential to the correct operation of this website, while others are optional (e.g., marketing cookies). We hope you'll accept them all, but it's entirely up to you. You're in control, and can accept or deny cookies as you see fit.
Functional
Always active
These cookies are essential for this website to function correctly while serving you.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.