Problem
I am trying to write a simple program in C, using OpenGL, that would allow "drawing" a 2D C array (int **, 32-bit integers) according to a color palette.
For the moment (I am not there yet, far from it :) ) I'm learning how to send an array of 32-bits signed ints to the GPU and show it somehow.
I'm trying to do this in modern OpenGL.
My approach (bear with me as I just started learning these topics two days ago):
Geometry data consists of four vertices (vertices) for defining a rectangle based of two triangles (defining by picking the vertices using indices (indices)). The vertices data is also interleaved with 2D texture coordinates (for texture sampling in the shaders).
I do the generation and binding of VAO, VBO and EBO, to get the vertex data from RAM to VRAM.
Then, I create a 2D texture using glTexImage2D(), with internal format equal to GL_R32I as my C array is of type int **. I am not so sure about the format and type parameters, but I've set them to GL_RED_INTEGER and GL_UNSIGNED_INT, respectively.
In the fragment shader I'm trying to "read" the original integers by doing something like texture(texture1, TexCoord).r but probably this isn't right... also tried to cast that red component to float: (float) texture(texture1, TexCoord).r but does not work either. Just to give you some reassurance that might code does somethings right, leaving only FragColor = vec4(1.0f, 0.8f, 0.2f, 1.0f); in the fragment shader does show that colour, meaning I get a rectangle filling up the window with that color. So only when I start fiddling with the texture I get either a black screen or cyan RGB: (0, 1.0, 1.0, 1.0).
Note: My C array is named plane, and right now it is filled up with a left block of 0 values and a right block of 1s.
Right now, I'd be happy if I could hard code an if-statement inside the fragment shader that colored the 0s and 1s from the 32-bit plane into any two other colors. Then I think I could proceed to include a 1D texture with the color palette... as done here.
Code
pixel.h
#ifndef PIXEL_H
#define PIXEL_H
/*
To make sure there will be no header conflicts, you can define
GLFW_INCLUDE_NONE before the GLFW header to explicitly disable
inclusion of the development environment header. This also allows
the two headers to be included in any order.
*/
#define GLFW_INCLUDE_NONE
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <plane.h>
#include <utils.h>
#include <stdlib.h>
#include <stdio.h>
#endif
pixel.c
#include <pixel.h>
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 TexCoord;\n"
"uniform isampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.8f, 0.2f, 1.0f);\n"
" //FragColor = vec4(texture(texture1, TexCoord).r, 1.0f, 1.0f, 1.0f);\n"
"}\n\0";
int main(void)
{
// Window width and height.
const unsigned int width = 20;
const unsigned int height = 10;
// Before you can use most GLFW functions, the library must be initialized.
if (!glfwInit()) {
printf("Could not initialise GLFW library!");
exit(EXIT_FAILURE);
}
/*
* By default, the OpenGL context GLFW creates may have any version.
* You can require a minimum OpenGL version by setting the
* GLFW_CONTEXT_VERSION_MAJOR and GLFW_CONTEXT_VERSION_MINOR hints
* before creation. If the required minimum version is not supported
* on the machine, context (and window) creation fails.
*/
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create a GLFW window.
GLFWwindow* window = glfwCreateWindow(width, height, "pixel example", NULL, NULL);
if (!window)
{
printf("Window or OpenGL context creation failed!\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
// Before you can use the OpenGL API, you must have a current OpenGL context.
glfwMakeContextCurrent(window);
/*
* If you are using an extension loader library to access modern OpenGL
* then this is when to initialize it, as the loader needs a current
* context to load from. This example uses glad, but the same rule applies
* to all such libraries.
*/
gladLoadGL();
/*
* Set a framebuffer size callback to update the viewport when
* the window size changes.
*/
glfwSetFramebufferSizeCallback(window, fb);
/*
*
* Data to be drawn.
*
*/
int **plane = NewPlane(width, height);
PLANE(width, height, if (i < width / 2) plane[i][j] = 0; else plane[i][j] = 1;)
//plane[width/2][height/2] = 1;
//PLANE(width, height, printf("%d %d %d\n", i, j, plane[i][j]);)
printf("size of int: %ld bytes\n", sizeof(int));
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
}
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
}
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
printf("ERROR::SHADER::PROGRAM::LINKING_FAILED%s\n", infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// float vertices[] = {
// 1.0f, 1.0f, 0.0f, // top right
// 1.0f, -1.0f, 0.0f, // bottom right
// -1.0f, -1.0f, 0.0f, // bottom left
// -1.0f, 1.0f, 0.0f // top left
// };
float vertices[] = {
// positions // texture coords
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
// note that we start from 0!
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
printf("VAO: %d\n", VAO);
glGenBuffers(1, &VBO);
printf("VBO: %d\n", VBO);
glGenBuffers(1, &EBO);
printf("EBO: %d\n", EBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// glEnableVertexAttribArray(0);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
if (plane) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, plane);
}
/*
*
* Main loop
*
*/
while (!glfwWindowShouldClose(window))
{
// Check if Escape is pressed and signal to close the window.
input(window);
// The glClearColor function is a state-setting function
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// The glClear is a state-using function in that it uses the
// current state to retrieve the clearing color from.
glClear(GL_COLOR_BUFFER_BIT);
// Rendering goes here.
glUseProgram(shaderProgram);
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
plane.h
#ifndef PLANE_H
#define PLANE_H
#include <stdlib.h>
#include <stdio.h>
#define PLANE(width, height, A) {int i,j,_ii,_jj;for(i=0,_ii=width;i<_ii;i++)for(j=0,_jj=height;j<_jj;j++){A};}
int **NewPlane(int, int);
#endif
plane.c
#include <plane.h>
int **NewPlane(int width,int height)
{
int **a;
int i,j;
a = (int **)calloc((size_t)(width),sizeof(int *));
if (a == NULL) {
fprintf(stderr,"NewPlane: error in memory allocation\n");
exit(EXIT_FAILURE);
}
a[0] = (int *)calloc((size_t)((width)*(height)),sizeof(int));
if (a[0] == NULL) {
fprintf(stderr,"NewPlane: error in memory allocation\n");
exit(EXIT_FAILURE);
}
for (i=1,j=width; i < j; i++)
a[i] = a[i-1] + height;
return a;
}
Since integral textures cannot be interpolated, the minifying filter and magnification filter needs to be on of the "nearest" filters:
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
texture is an overloaded function. If you lookup a isampler2D with texture the type of the return value is ivec4:
uniform isampler2D texture1;
void main()
{
int value = texture(texture1, TexCoord).r;
// [...]
}
Since the internal data type is GL_R32I, the returned value is in the range of -2,147,483,648 to 2,147,483,647. The format of the default framebuffer is a unsigned normalized floating point format. The data in the buffer represent the values in the range [0.0, 1.0]. Therefore, you need to scale the integral value (maxValue should be the highest value in the texture):
FragColor = vec4(float(value) / maxValue, 1.0f, 1.0f, 1.0f);
One way get colors form an integral texture is to create a 1-dimensional texture that is a table of colors and use the integral value from the texture to lookup a color in the table:
uniform isampler2D indexTexture;
uniform sampler1D colorTable;
void main()
{
int index = texture(indexTexture, TexCoord).r;
vec4 color = texelFetch(colorTable, index, 0);
// [...]
}
Related
Problem
I am trying to write a simple program in C, using OpenGL, that would allow "drawing" a 2D C array (int **, 32-bit integers) according to a color palette.
For the moment (I am not there yet, far from it :) ) I'm learning how to send an array of 32-bits signed ints to the GPU and show it somehow.
I'm trying to do this in modern OpenGL.
My approach (bear with me as I just started learning these topics two days ago):
Geometry data consists of four vertices (vertices) for defining a rectangle based of two triangles (defining by picking the vertices using indices (indices)). The vertices data is also interleaved with 2D texture coordinates (for texture sampling in the shaders).
I do the generation and binding of VAO, VBO and EBO, to get the vertex data from RAM to VRAM.
Then, I create a 2D texture using glTexImage2D(), with internal format equal to GL_R32I as my C array is of type int **. I am not so sure about the format and type parameters, but I've set them to GL_RED_INTEGER and GL_UNSIGNED_INT, respectively.
In the fragment shader I'm trying to "read" the original integers by doing something like texture(texture1, TexCoord).r but probably this isn't right... also tried to cast that red component to float: (float) texture(texture1, TexCoord).r but does not work either. Just to give you some reassurance that might code does somethings right, leaving only FragColor = vec4(1.0f, 0.8f, 0.2f, 1.0f); in the fragment shader does show that colour, meaning I get a rectangle filling up the window with that color. So only when I start fiddling with the texture I get either a black screen or cyan RGB: (0, 1.0, 1.0, 1.0).
Note: My C array is named plane, and right now it is filled up with a left block of 0 values and a right block of 1s.
Right now, I'd be happy if I could hard code an if-statement inside the fragment shader that colored the 0s and 1s from the 32-bit plane into any two other colors. Then I think I could proceed to include a 1D texture with the color palette... as done here.
Code
pixel.h
#ifndef PIXEL_H
#define PIXEL_H
/*
To make sure there will be no header conflicts, you can define
GLFW_INCLUDE_NONE before the GLFW header to explicitly disable
inclusion of the development environment header. This also allows
the two headers to be included in any order.
*/
#define GLFW_INCLUDE_NONE
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <plane.h>
#include <utils.h>
#include <stdlib.h>
#include <stdio.h>
#endif
pixel.c
#include <pixel.h>
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 TexCoord;\n"
"uniform isampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.8f, 0.2f, 1.0f);\n"
" //FragColor = vec4(texture(texture1, TexCoord).r, 1.0f, 1.0f, 1.0f);\n"
"}\n\0";
int main(void)
{
// Window width and height.
const unsigned int width = 20;
const unsigned int height = 10;
// Before you can use most GLFW functions, the library must be initialized.
if (!glfwInit()) {
printf("Could not initialise GLFW library!");
exit(EXIT_FAILURE);
}
/*
* By default, the OpenGL context GLFW creates may have any version.
* You can require a minimum OpenGL version by setting the
* GLFW_CONTEXT_VERSION_MAJOR and GLFW_CONTEXT_VERSION_MINOR hints
* before creation. If the required minimum version is not supported
* on the machine, context (and window) creation fails.
*/
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create a GLFW window.
GLFWwindow* window = glfwCreateWindow(width, height, "pixel example", NULL, NULL);
if (!window)
{
printf("Window or OpenGL context creation failed!\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
// Before you can use the OpenGL API, you must have a current OpenGL context.
glfwMakeContextCurrent(window);
/*
* If you are using an extension loader library to access modern OpenGL
* then this is when to initialize it, as the loader needs a current
* context to load from. This example uses glad, but the same rule applies
* to all such libraries.
*/
gladLoadGL();
/*
* Set a framebuffer size callback to update the viewport when
* the window size changes.
*/
glfwSetFramebufferSizeCallback(window, fb);
/*
*
* Data to be drawn.
*
*/
int **plane = NewPlane(width, height);
PLANE(width, height, if (i < width / 2) plane[i][j] = 0; else plane[i][j] = 1;)
//plane[width/2][height/2] = 1;
//PLANE(width, height, printf("%d %d %d\n", i, j, plane[i][j]);)
printf("size of int: %ld bytes\n", sizeof(int));
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
}
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
}
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
printf("ERROR::SHADER::PROGRAM::LINKING_FAILED%s\n", infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// float vertices[] = {
// 1.0f, 1.0f, 0.0f, // top right
// 1.0f, -1.0f, 0.0f, // bottom right
// -1.0f, -1.0f, 0.0f, // bottom left
// -1.0f, 1.0f, 0.0f // top left
// };
float vertices[] = {
// positions // texture coords
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
// note that we start from 0!
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
printf("VAO: %d\n", VAO);
glGenBuffers(1, &VBO);
printf("VBO: %d\n", VBO);
glGenBuffers(1, &EBO);
printf("EBO: %d\n", EBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// glEnableVertexAttribArray(0);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
// uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
if (plane) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, plane);
}
/*
*
* Main loop
*
*/
while (!glfwWindowShouldClose(window))
{
// Check if Escape is pressed and signal to close the window.
input(window);
// The glClearColor function is a state-setting function
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// The glClear is a state-using function in that it uses the
// current state to retrieve the clearing color from.
glClear(GL_COLOR_BUFFER_BIT);
// Rendering goes here.
glUseProgram(shaderProgram);
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
plane.h
#ifndef PLANE_H
#define PLANE_H
#include <stdlib.h>
#include <stdio.h>
#define PLANE(width, height, A) {int i,j,_ii,_jj;for(i=0,_ii=width;i<_ii;i++)for(j=0,_jj=height;j<_jj;j++){A};}
int **NewPlane(int, int);
#endif
plane.c
#include <plane.h>
int **NewPlane(int width,int height)
{
int **a;
int i,j;
a = (int **)calloc((size_t)(width),sizeof(int *));
if (a == NULL) {
fprintf(stderr,"NewPlane: error in memory allocation\n");
exit(EXIT_FAILURE);
}
a[0] = (int *)calloc((size_t)((width)*(height)),sizeof(int));
if (a[0] == NULL) {
fprintf(stderr,"NewPlane: error in memory allocation\n");
exit(EXIT_FAILURE);
}
for (i=1,j=width; i < j; i++)
a[i] = a[i-1] + height;
return a;
}
I got it working now, there were a few things that needed to be fixed.
As #Rabbid76 indicated, the conversion of the integral values in value (in the fragment shader code) to floating-point numbers within the range [0, 1] was an important change.
Also, as pointed out by #tstanisl, the data passed to glTexImage2D() needs to be contiguous in memory, so, in my case, changing the call to glTexImage2D(..., plane[0]) was also important.
Finally, I was missing:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
as explained in GL_R32I TexImage2D not uploading correctly.
texture is an overloaded function. If you lookup a isampler2D with texture the type of the return value is ivec4:
uniform isampler2D texture1;
void main()
{
int value = texture(texture1, TexCoord).r;
// [...]
}
Since the internal data type is GL_R32I, the returned value is in the range of -2,147,483,648 to 2,147,483,647. However, the color channel for the default framebuffer must be in the range [0.0, 1.0]. Therefore, you need to scale the integral value (maxValue should be the highest value in the texture):
FragColor = vec4(float(value) / maxValue, 1.0f, 1.0f, 1.0f);
Integral textures cannot be interpolated, therefore the minifying filter and magnification filter must to be GL_NEAREST (or GL_NEAREST_MIPMAP_NEAREST in case of GL_TEXTURE_MIN_FILTER):
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
I've just started to experiment with OpenGL (using freeglut and GLEW). I can get a window to pop up, but nothing gets drawn to it, I can't even get it to change background color.
Here's what the main function looks like:
int
main(int argc, char **argv)
{
GLenum err;
/* Initialize GLUT library */
glutInit(&argc, argv);
/* Set window mode */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
/* Choose OpenGL version */
glutInitContextVersion(3, 3);
/* Choose OpenGL profile */
glutInitContextFlags(GLUT_CORE_PROFILE);
/* Create a GLUT window */
glutCreateWindow("OpenGL 2D");
/* Initialize GLEW library */
glewExperimental = GL_TRUE;
err = glewInit();
if(err != GLEW_OK) {
printf("Cannot initialize GLEW: %s\n",
glewGetErrorString(err));
return 1;
}
/* WORKAROUND: Ignore all GLEW errors */
while(glGetError() != GL_NO_ERROR)
;
(void) printf("OpenGL vendor: \"%s\"\n"
" renderer: \"%s\"\n"
" version: \"%s\"\n"
" SL version: \"%s\"\n",
glGetString(GL_VENDOR),
glGetString(GL_RENDERER),
glGetString(GL_VERSION),
glGetString(GL_SHADING_LANGUAGE_VERSION));
/* Set up callback function for "reshape" event */
printf("Setting callback functions\n");
glutReshapeFunc(canvas_reshape);
/* Set up callback function for "display" event */
glutDisplayFunc(canvas_display);
/* Set up callback function for "keyboard" event */
glutKeyboardFunc(canvas_keyboard);
/* Set up callback function for "timer" event */
glutTimerFunc(30, canvas_timer, 0);
/* Initialize OpenGL */
init();
/* Choose the window's position */
glutPositionWindow(100, 100);
/* Choose the window's size */
glutReshapeWindow(800, 600);
/* Start main loop */
printf("Entering main loop\n");
glutMainLoop();
return 0;
}
Here's what init looks like:
init()
{
const GLchar *vertex_shader_source[] = {
"#version 330 core\n",
"\n",
"layout(location=0) in vec4 position;\n",
"out vec4 color;\n",
"uniform mat4 viewtrans;\n",
"\n",
"void\n",
"main()\n",
"{\n",
" gl_Position = viewtrans * position;\n"
" color = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n" };
const GLchar *fragment_shader_source[] = {
"#version 330 core\n",
"in vec4 color;\n",
"layout(location=0) out vec4 fcolor;\n",
"void\n",
"main()\n",
"{\n",
" fcolor = color;\n",
"}\n" };
char compiler_log[LOGSIZE];
/* Obtain an unused name for our vertex array */
printf("Creating vertex array\n");
glGenVertexArrays(1, &vertex_array);
check_error("glGenVertexArrays");
/* Tell OpenGL to use the new vertex array */
glBindVertexArray(vertex_array);
check_error("glBindVertexArray");
/* Obtain an unused name for our vertex buffer */
printf("Creating vertex buffer\n");
glGenBuffers(1, &vertex_buffer);
check_error("glGenBuffers");
/* Tell OpenGL to use the new vertex buffer as the
* vertex array */
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
check_error("glBindBuffer");
/* We want to get two coordinates from the vertex buffer and
* feed them as the first parameter to the vertex shader */
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, 0);
/* Create vertex shader */
printf("Creating vertex shader\n");
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
check_error("glCreateShader");
/* Assign vertex shader source code */
glShaderSource(vertex_shader, sizeof(vertex_shader_source) / sizeof(GLchar *),
vertex_shader_source, 0);
check_error("glShaderSource");
/* Compile vertex shader */
glCompileShader(vertex_shader);
check_error("glCompileShader");
/* Get compiler log *//* Set up callback function for "timer" event */
glGetShaderInfoLog(vertex_shader, LOGSIZE, 0, compiler_log);
printf(" Compiler log: \"%s\"\n", compiler_log);
/* Create fragment shader */
printf("Creating fragment shader\n");
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
check_error("glCreateShader");
/* Assign fragment shader source code */
glShaderSource(fragment_shader, sizeof(fragment_shader_source) / sizeof(GLchar *),
fragment_shader_source, 0);
check_error("glShaderSource");
/* Compile fragment shader */
glCompileShader(fragment_shader);
check_error("glCompileShader");
/* Get compiler log */
glGetShaderInfoLog(fragment_shader, LOGSIZE, 0, compiler_log);
printf(" Compiler log: \"%s\"\n", compiler_log);
/* Create shader program */
printf("Creating shader program\n");
shader_program = glCreateProgram();
check_error("glCreateProgram");
/* Attach vertex shader */
glAttachShader(shader_program, vertex_shader);
check_error("glAttachShader");
/* Attach fragment shader */
glAttachShader(shader_program, fragment_shader);
check_error("glAttachShader");
/* Link shader program */
glLinkProgram(shader_program);
check_error("glLinkProgram");
/* Get linker log */
glGetProgramInfoLog(shader_program, LOGSIZE, 0, compiler_log);
printf(" Linker log: \"%s\"\n", compiler_log);
/* Get location of "viewtrans" matrix */
viewtrans = glGetUniformLocation(shader_program, "viewtrans");
check_error("glGetUniformLocation");
/* Tell OpenGL to use the new shader program */
glUseProgram(shader_program);
/* Choose background color */
glClearColor(1.0, 0.0, 1.0, 0.0);
}
Here's what I use to draw:
static void
canvas_display()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
This is all based off of a tutorial. What actually happens is something like a 300 x 300 white window shows up, gets reshaped into a 800 x 600 black window, BUT with still a 300 x 300 white square in the middle or in a corner, whose pixels (partially) turn black when resizing the window such that (part of) the square disappears (either manually or through code). I've also written code to draw triangles, but that doesn't work either, as one might expect. But here's the code for that anyway:
static void
canvas_display()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat vertices[] =
{ 0.0, 0.0,
0.5, 0.5,
0.5, 0.0 };
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
Do not continuously specify the new vertex array objects. Create 1 Vertex Array Object at initialization. The vertex array is contained in the Vertex Buffer Object. When a named buffer object is bound to the ARRAY_BUFFER target, then the last parameter of glVertexAttribPointer is treated as a byte offset into the buffer object's data store. Thus the offset has to be NULL:
void init()
{
// [...]
GLfloat vertices[] =
{ 0.0, 0.0,
0.5, 0.5,
0.5, 0.0 };
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 2, NULL);
}
Ensure that the program is installed and the VAO is bound when the geometry is drawn. By default the values of uniforms are initialized by zero. At least you have to set the Identity matrix then matrix uniform viewtrans.
Since you use double buffering (GLUT_DOUBLE), you have to swaps the buffers of the current window after drawing all the geometry by glutSwapBuffers
void canvas_display()
{
glClearColor(1.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shader_program);
GLfloat identity_mat[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
glUniformMatrix4fv(viewtrans, 1, GL_FALSE, identity_mat);
glBindVertexArray(vertex_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glutSwapBuffers();
glutPostRedisplay();
}
I'm trying to write a code to draw a triangle with changing color, following the tutorial LearnOpenGL - Shaders.
But my code doesn't work, the triangle is blinking instead of gradually change the color, I don't know if it is a hardware or software problem.
Can anyone help me?
I'm using OpenGL 3.0 with Mesa 18.3.4.
And compiling with gcc shaders_change.c -lGL -lglfw -lm
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/gl.h>
#include <GLFW/glfw3.h>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char *vertexShaderSource ="#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
"}\n\0";
const char *fragmentShaderSource = "#version 330\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"
"void main()\n"
"{\n"
" FragColor = ourColor;\n"
"}\n\0";
int main(){
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
printf("Failed to create GLFW window");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// build and compile our shader program
// ------------------------------------
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s",infoLog);
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s",infoLog);
}
// link shaders
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s",infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
// positions // colors
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, // top
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
// color attribute
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
//glEnableVertexAttribArray(VAO);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
// as we only have a single shader, we could also just activate our shader once beforehand if we want to
glBindVertexArray(VAO);
// render loop
// -----------
float timeValue;
float greenValue;
int vertexColorLocation;
while (!glfwWindowShouldClose(window)){
// input
// -----
processInput(window);
// render
// ------
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
timeValue = glfwGetTime();
greenValue = sin(timeValue) / 2.0f + 0.5f;
vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
// render the triangle
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window){
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, 1);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height){
glViewport(0, 0, width, height);
}
thank you all for help!!
I'd discover what is my problem, I'd just forgot to include de glad.h library and the libdl.
I do not understand how this main function works. I have a display function, which uses glDrawArrays, but I do not see it being called. I only see it being used as a parameter for glutDisplayFunction.
Here is my main:
int main(int argc, char** argv){
// Set up the window
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Hello Triangle");
// Tell glut where the display function is
glutDisplayFunc(display);
// A call to glewInit() must be done after glut is initialized!
GLenum res = glewInit();
// Check for any errors
if (res != GLEW_OK) {
fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
return 1;
}
// Set up your objects and shaders
init();
// Begin infinite event loop
glutMainLoop();
return 0;
}
The problem is, I need to create two different triangles, on the same window, using seperate VAOs and VBOs. I've created the seperate VAO and VBO for my second triangle. However, I do not see how I am meant to generate and link my buffers, draw my arrays, switch to my second buffer, and draw those arrays, when I do not even know when my display function is being called.
My display function looks like this:
void display(){
glClear(GL_COLOR_BUFFER_BIT);
// NB: Make the call to draw the geometry in the currently activated vertex buffer. This is where the GPU starts to work!
glDrawArrays(GL_TRIANGLES, 0, 3);
glutSwapBuffers();
}
All operations could be done in separate function named asyouwant called from main
example:
#include <GL/glut.h>
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitWindowSize(300, 300);
glutInitWindowPosition(100, 100);
glutCreateWindow("Hello world :D");
glutDisplayFunc(displayMe); // = > draw in displayme function
glutMainLoop();
return 0;
}
void displayMe(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POLYGON);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.5, 0.0, 0.0);
glVertex3f(0.5, 0.5, 0.0);
glVertex3f(0.0, 0.5, 0.0);
glEnd();
// a second geoform
glBegin(GL_POLYGON);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(-0.5, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0.0);
glVertex3f(0.0, -0.5, 0.0);
glEnd();
glFlush();
}
As complement: for VAO and buffer
1- Init (declare VAO , declare buffer of vertices, ...)
GLuint VaoID;
glGenVertexArrays(1, &VaoID);
glBindVertexArray(VaoID);
// An array of 3 vectors which represents 3 vertices
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
Once time only
// This will identify our vertex buffer
GLuint vertexbuffer;
// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &vertexbuffer);
// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
2- Use it (bind and draw in display fucntion)
// 1st attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, 3); // Starting from vertex 0; 3 vertices total -> 1 triangle
glDisableVertexAttribArray(0);
I'm having issues loading a texture onto my triangle strips. I'm following Anton Gerdelan's tutorial, and after failing with my main program, I went back to the basics and just tried to make a plain square and put his texture on it (the skull and crossbones).
I completely copy and pasted code from his "Hello Triangle" page, which worked, but once trying to fit in code from his texture tutorial above (and changing the triangle to a square), all I'm getting is a big white square with no texture.
I've checked the status of my shaders with glGetShaderiv() and they returned positive, I checked the image I loaded to see if the pixel data was sensible, so I believe my error is in declaring my VBOs, or the order/parameters in which I'm using them.
Here's the complete code which I copied, which compiles fine in Visual Studio 2013, except the output isn't what is expected.
I am using the static libraries of GLEW and GLFW, along with the STBI Image header
#include <GL/glew.h> // include GLEW and new version of GL on Windows
#include <GL/glfw3.h> // GLFW helper library
#include <stdio.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
const char* vertex_shader =
"#version 400\n"
"in vec3 vp;"
"layout (location=1) in vec2 vt; // per-vertex texture co-ords"
"out vec2 texture_coordinates; "
"void main () {"
" gl_Position = vec4 (vp, 1.0);"
" texture_coordinates = vt; "
"}";
const char* fragment_shader =
"#version 400\n"
"in vec2 texture_coordinates;"
"uniform sampler2D basic_texture;"
"out vec4 frag_colour;"
"void main () {"
"vec4 texel = texture(basic_texture, texture_coordinates);"
"frag_colour = texel; "
"}";
float points[] = {
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f
};
float texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
};
GLFWwindow* window;
unsigned int vt_vbo;
unsigned int tex = 0;
GLuint vao = 0;
GLuint vbo = 0;
GLuint shader_programme;
void initializeGL(){
// start GL context and O/S window using the GLFW helper library
if (!glfwInit()) {
printf("ERROR: could not start GLFW3\n");
return;
}
window = glfwCreateWindow(640, 480, "Texture Test", NULL, NULL);
if (!window) {
printf("ERROR: could not open window with GLFW3\n");
glfwTerminate();
return;
}
glfwMakeContextCurrent(window);
// start GLEW extension handler
glewExperimental = GL_TRUE;
glewInit();
// get version info
const GLubyte* renderer = glGetString(GL_RENDERER); // get renderer string
const GLubyte* version = glGetString(GL_VERSION); // version as a string
printf("Renderer: %s\n", renderer);
printf("OpenGL version supported %s\n", version);
// tell GL to only draw onto a pixel if the shape is closer to the viewer
glEnable(GL_DEPTH_TEST); // enable depth-testing
glDepthFunc(GL_LESS); // depth-testing interprets a smaller value as "closer"
}
void startShaders(){
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertex_shader, NULL);
glCompileShader(vs);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragment_shader, NULL);
glCompileShader(fs);
shader_programme = glCreateProgram();
glAttachShader(shader_programme, fs);
glAttachShader(shader_programme, vs);
glLinkProgram(shader_programme);
GLint vsstat;
glGetShaderiv(vs, GL_COMPILE_STATUS, &vsstat);
GLint fsstat;
glGetShaderiv(fs, GL_COMPILE_STATUS, &fsstat);
printf("%i\n%i\n", vsstat, fsstat);
}
void loadImage(){
int x, y, n;
int force_channels = 4;
unsigned char* image_data = stbi_load("skulluvmap.png", &x, &y, &n, force_channels);
if (!image_data) {
printf("ERROR: could not load %s\n", "skulluvmap.png");
}
int width_in_bytes = x * 4;
unsigned char *top = NULL;
unsigned char *bottom = NULL;
unsigned char temp = 0;
int half_height = y / 2;
for (int row = 0; row < half_height; row++) {
top = image_data + row * width_in_bytes;
bottom = image_data + (y - row - 1) * width_in_bytes;
for (int col = 0; col < width_in_bytes; col++) {
temp = *top;
*top = *bottom;
*bottom = temp;
top++;
bottom++;
}
}
printf("first 4 bytes are: %i %i %i %i\n",
image_data[0], image_data[1], image_data[2], image_data[3]
);
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void generateBuffers(){
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), points, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0); // don't forget this!
glGenBuffers(1, &vt_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), texcoords, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(1); // don't forget this!
}
void mainLoop(){
while (!glfwWindowShouldClose(window)) {
// wipe the drawing surface clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int tex_loc = glGetUniformLocation(shader_programme, "basic_texture");
glUseProgram(shader_programme);
glUniform1i(tex_loc, 0); // use active texture 0
// draw points 0-4 from the currently bound VAO with current in-use shader
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// update other events like input handling
glfwPollEvents();
// put the stuff we've been drawing onto the display
glfwSwapBuffers(window);
}
}
int main() {
initializeGL();
startShaders();
loadImage();
generateBuffers();
mainLoop();
// close GL context and any other GLFW resources
glfwTerminate();
return 0;
}
You're misusing your second buffer which is supposed to be the buffer with texcoords. So what you really want to achieve is having a pair of texture coordinates for every vertex. It means that you texcoords array should in fact store 4 pairs because you have 4 triples in the points array. So that's the first fix. You probably want it to look like:
float texcoords[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0, 0.0,
1.0, 1.0,
};
Then in the generateBuffers, your vt_vbo is wrong. The data should be passed this way:
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(float), texcoords, GL_STATIC_DRAW);
because you only want to pass 8 values there. 2 texcoords for each vertex.
Edit:
This however, doesn't fully explain why your texture doesn't appear at all. I primarily thought that there might be a problem with your texcoords pointer but it doesn't seem to be the case.