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);
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);
// [...]
}
Below I've included glfw-quick-test.c, which is basically copied verbatim from http://www.glfw.org/docs/latest/quick.html - except for removed use of glad, added background color, a couple of defines so it compiles on Ubuntu 14.04 (64-bit), and a preprocessor #ifdef switch (macro DO_OPENGL_THREE) to change requested OpenGL version.
When I compile with:
gcc -g -o glfw-quick-test.exe glfw-quick-test.c -lglfw -lGLU -lGL -lm
./glfw-quick-test.exe
... then I get the message "GLFW Requesting OpenGL 2.1" and drawing is fine:
When I compile with:
gcc -g -DDO_OPENGL_THREE -o glfw-quick-test.exe glfw-quick-test.c -lglfw -lGLU -lGL -lm
./glfw-quick-test.exe
... then I get the message "GLFW Requesting OpenGL 3.2" and the rotating triangle is not drawn at all - only the background color is preserved:
Can anyone explain why does this happen? Can I somehow get GLFW3 to draw even if OpenGL 3.2 is requested, and if so, how?
(I'm aware that the original source code says "// NOTE: OpenGL error checks have been omitted for brevity", but I'm not sure what sort of error checks I should add, to see what the problem would be with OpenGL 3.2 drawing ...)
The code, glfw-quick-test.c (EDIT: now with error checking):
// http://www.glfw.org/docs/latest/quick.html
#define UBUNTU14
#ifdef UBUNTU14 // assume Ubuntu 14.04
// strange; Ubuntu 14 GLFW/glfw3.h doesn't have GLFW_TRUE, GLFW_FALSE, mentions GL_TRUE GL_FALSE
#define GLFW_TRUE GL_TRUE
#define GLFW_FALSE GL_FALSE
#endif
//~ #include <glad/glad.h> // "GL/GLES/EGL/GLX/WGL Loader-Generator based on the official specs."
#include <GLFW/glfw3.h>
#include "linmath.h"
#include <stdlib.h>
#include <stdio.h>
static const struct
{
float x, y;
float r, g, b;
} vertices[3] =
{
{ -0.6f, -0.4f, 1.f, 0.f, 0.f },
{ 0.6f, -0.4f, 0.f, 1.f, 0.f },
{ 0.f, 0.6f, 0.f, 0.f, 1.f }
};
static const char* vertex_shader_text =
"uniform mat4 MVP;\n"
"attribute vec3 vCol;\n"
"attribute vec2 vPos;\n"
"varying vec3 color;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
" color = vCol;\n"
"}\n";
static const char* fragment_shader_text =
"varying vec3 color;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(color, 1.0);\n"
"}\n";
static void error_callback(int error, const char* description)
{
fprintf(stderr, "Error: %s\n", description);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
void checkGLerrors(char *label) {
// check OpenGL error
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
char* errorstr = "";
switch(err) {
case GL_INVALID_OPERATION: errorstr="INVALID_OPERATION"; break;
case GL_INVALID_ENUM: errorstr="INVALID_ENUM"; break;
case GL_INVALID_VALUE: errorstr="INVALID_VALUE"; break;
case GL_OUT_OF_MEMORY: errorstr="OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: errorstr="INVALID_FRAMEBUFFER_OPERATION"; break;
}
printf("OpenGL error ('%s'): %d %s\n", label, err, errorstr);
}
}
int main(void)
{
GLFWwindow* window;
GLuint vertex_buffer, vertex_shader, fragment_shader, program;
GLint mvp_location, vpos_location, vcol_location;
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
// NB: Ubuntu will not draw if 3.2 (just black screen) - only if 2.0 or 2.1?
#ifdef DO_OPENGL_THREE
printf("GLFW Requesting OpenGL 3.2\n");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // only 3.2+
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); //only 3.0+
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // https://stackoverflow.com/q/23834680/
#else
printf("GLFW Requesting OpenGL 2.1\n");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // 0);
#endif
checkGLerrors("post hint");
window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
checkGLerrors("post glfwCreateWindow");
glfwSetKeyCallback(window, key_callback);
glfwMakeContextCurrent(window);
//~ gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
glfwSwapInterval(1);
// NOTE: OpenGL error checks have been omitted for brevity
glGenBuffers(1, &vertex_buffer);
checkGLerrors("post glGenBuffers");
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
glCompileShader(vertex_shader);
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
glCompileShader(fragment_shader);
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
checkGLerrors("post glLinkProgram");
mvp_location = glGetUniformLocation(program, "MVP");
vpos_location = glGetAttribLocation(program, "vPos");
vcol_location = glGetAttribLocation(program, "vCol");
checkGLerrors("post gl locations");
glEnableVertexAttribArray(vpos_location);
checkGLerrors("post gl EnableVertexAttribArray");
glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
sizeof(float) * 5, (void*) 0);
checkGLerrors("post glVertexAttribPointer");
glEnableVertexAttribArray(vcol_location);
checkGLerrors("post glEnableVertexAttribArray");
glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
sizeof(float) * 5, (void*) (sizeof(float) * 2));
checkGLerrors("post glVertexAttribPointer");
while (!glfwWindowShouldClose(window))
{
float ratio;
int width, height;
mat4x4 m, p, mvp;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glViewport(0, 0, width, height);
glClearColor(0.784314, 0.780392, 0.305882, 1.0); // add background color
glClear(GL_COLOR_BUFFER_BIT);
mat4x4_identity(m);
mat4x4_rotate_Z(m, m, (float) glfwGetTime());
mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
mat4x4_mul(mvp, p, m);
glUseProgram(program);
glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
Thanks to #BDL and his comment about VAO (vertex array objects), I found How to use VBOs without VAOs with OpenGL core profile? - and by trying out stuff from there, I found that the only change to the above OP code, needed so drawing is shown in OpenGL 3.2, is this:
...
//~ gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
glfwSwapInterval(1);
#ifdef DO_OPENGL_THREE
// https://stackoverflow.com/a/30057424/277826:
// "You can however just create and bind a VAO and forget about it (keep it bound)."
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
#endif
// NOTE: OpenGL error checks have been omitted for brevity
glGenBuffers(1, &vertex_buffer);
...
Once this section with VAO is in, there are no more OpenGL errors printed, and drawing is OK.
I'm using OpenGL 3 and Glew in order to draw a triangle, I have a window (changing the background color works fine) but I can't put my shader on it. I did some tests like:
glGetProgramiv(shader_programme, GL_LINK_STATUS, &isLinked);
printf("\nProg : %i",isLinked);
And it's fine; print returns 1 for the program, the vertex and the frag.
I suppose I missed a clear somewhere, but I'm not sure and also pretty lost here...
This is my code:
#include "../include/scop.h"
#include <OpenGL/gl.h>
#include ".../lfw3/3.2.1/include/GLFW/glfw3.h"
t_scop *ft_init_window(t_scop *scop, t_parse parse)
{
if (!glfwInit())
ft_putstr("error init");
else
{
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
WIN = glfwCreateWindow(WIN_X, WIN_Y, "Scop", NULL, NULL);
glfwMakeContextCurrent(WIN);
glfwSetInputMode(WIN, GLFW_STICKY_KEYS, GL_TRUE);
glfwSetInputMode(WIN, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwPollEvents();
glfwSetCursorPos(WIN, WIN_X / 2.0, WIN_Y / 2.0);
glClearColor(0.0f, 0.5f, 0.4f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
}
float points[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
//init buffer and fill it
GLuint vbo = 0;
glGenBuffers (1, &vbo);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, 9 * sizeof (float), points, GL_STATIC_DRAW);
//init VertexArray
GLuint vao = 0;
glGenVertexArraysAPPLE (1, &vao);
glBindVertexArrayAPPLE (vao);
glEnableVertexAttribArray (0);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glDrawArrays(GL_TRIANGLES, 0, 3);
const char* vertex_shader =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main () {"
"gl_Position.xyz = position;"
"gl_Position.w = 1.0;"
"}\0";
const char* fragment_shader =
"#version 330 core\n"
"out vec3 color;"
"void main () {"
"color = vec3(1,0,0);"
"}\0";
//create vertex
GLuint vs = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vs, 1, &vertex_shader, NULL);
glCompileShader (vs);
//tests
GLint success = 0;
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
printf ("Taille du source:%i\n", success);
if (GL_FALSE == success)
printf("false");
else printf("true");
//create frag
GLuint fs = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fs, 1, &fragment_shader, NULL);
glCompileShader (fs);
//tests
success = 0;
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
printf("Taille fs : %i",success);
// GLuint shader_programme = LoadShaders (vs,fs);
GLint shader_programme = glCreateProgram ();
glAttachShader (shader_programme, vs);
glAttachShader (shader_programme, fs);
glLinkProgram (shader_programme);
//tests
GLint isLinked = 0;
glGetProgramiv(shader_programme, GL_LINK_STATUS, &isLinked);
printf("\nProg : %i",isLinked);
//idk if i need to do this now
glDetachShader(shader_programme, vs);
glDetachShader(shader_programme, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glGetError();
while (!glfwWindowShouldClose(WIN))
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearDepth(GL_DEPTH_TEST);
glUseProgram (shader_programme);
glBindVertexArrayAPPLE (vao);
glDrawArrays (GL_TRIANGLES, 0, 3);
//glUseProgram(0); ???
glfwPollEvents ();
glBindVertexArrayAPPLE (0);
glfwSwapBuffers(WIN);
}
// glfwTerminate();
return (scop);
}
Any help is greatly appreciated!
The problem lies in this line:
glClearDepth(GL_DEPTH_TEST);
glClearDepth (doc) specifies with which value the depth buffer should be cleared and expects a floating point value between 0 and 1. It is the similar to glClearColor, just for depth.
Additionally, you should be using the core profile VAO functions instead of the ones from the APPLE extension. The apple extension should only be used in a OpenGL context <= 2.1.
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.