OpenGL4 gl_texture_2d_array won't display - c

I want to get started with modern OpenGL while reading the superbible book by coding a cellular automata using a 2d-array texture of size 1280x1280x2 and computing the next state to another layer in a compute shader. The idea is impiously stolen from glumpy examples.
However, while having such ambition in mind, I got confused at even trying to display it, not even passing the samplers into shaders.
Below I included both, the generator, which works OK, and the piece of code that does contain a problem.
gen
#!/usr/bin/env perl
use strict;
use warnings;
sub get_arg {
return (scalar #ARGV == 0) ? shift : shift #ARGV;
}
my $size = get_arg 1280;
my $rate = get_arg ($size >> 1);
my $symbol = (sub { ((shift) < $rate) ? '*' : '_' } );
print "$size\n";
for (0..$size) {
print $symbol->(int(rand() * $size)) for (0..$size);
print "\n";
}
code
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
// including opengl libraries on linux/osx
//include glew
#include <GL/glew.h>
//include opengl
#if defined (__APPLE_CC__)
#include <OpenGL/gl3.h>
#else
#include <GL/gl3.h> /* assert OpenGL 3.2 core profile available. */
#endif
//include glfw3
#define GLFW_INCLUDE_GL3 /* don't drag in legacy GL headers. */
#define GLFW_NO_GLU /* don't drag in the old GLU lib - unless you must. */
#include <GLFW/glfw3.h>
// ----------- the program itself
GLFWwindow *g_window;
#define SIZE 1280
#define WIDTH SIZE
#define HEIGHT SIZE
#define DEPTH 2
init_glfw(const char *name) {
// start GL context and O/S window using the GLFW helper library
assert(glfwInit());
#if defined(__APPLE_CC__)
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);
#endif
g_window = glfwCreateWindow(WIDTH, HEIGHT, name, NULL, NULL);
assert(g_window != NULL);
glfwMakeContextCurrent(g_window);
// start GLEW extension handler
glewExperimental = GL_TRUE;
glewInit();
// 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"
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
typedef enum { FILLED = '*', EMPTY = '_' } SYMBOL;
load_array(GLubyte array[SIZE * SIZE * DEPTH], FILE *stream) {
static char c;
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
bool approved = false;
GLubyte *it = &array[SIZE * i + j];
while(!approved) {
approved = true;
c = getc(stream);
assert(c != EOF);
switch(c) {
case FILLED:
*it = 0x00;
break;
case EMPTY:
*it = 0xff;
break;
default:
approved = false;
break;
}
}
assert(*it == 0x00 || *it == 0xff);
it[SIZE * SIZE] = it[0];
}
}
}
GLuint create_2d_texture() {
static GLuint texture = 0;
assert(texture == 0);
static GLubyte field[SIZE * SIZE << 1];
load_array(field, stdin);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_ALPHA8, SIZE, SIZE, DEPTH);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, SIZE, SIZE, DEPTH, GL_ALPHA, GL_UNSIGNED_BYTE, field);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
return texture;
}
display() {
GLuint texture = create_2d_texture();
assert(texture != 0);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT_AND_BACK);
while(!glfwWindowShouldClose(g_window)) {
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glPushMatrix();
glEnable(GL_TEXTURE_2D_ARRAY);
glBegin(GL_QUADS);
glTexCoord3s(0, SIZE, 0); glVertex3f( 0.0f, 0.0f, 0.0 );
glTexCoord3s(SIZE, SIZE, 0); glVertex3f( SIZE, 0.0f, 0.0 );
glTexCoord3s(0, SIZE, 0); glVertex3f( SIZE, SIZE, 0.0 );
glTexCoord3s(0, 0, 0); glVertex3f( 0.0f, SIZE, 0.0 );
glEnd();
glPopMatrix();
glfwSwapBuffers(g_window);
glfwPollEvents();
if(glfwGetKey(g_window, GLFW_KEY_ESCAPE)) {
glfwSetWindowShouldClose(g_window, 1);
}
}
}
main() {
init_glfw("I want to display a texture");
display();
glfwDestroyWindow(g_window);
glfwTerminate();
}
Could you help me to analyse the issues with displaying the 2d array on the screen, please? What I am trying to achieve is to make the whole window randomly black-and-white, but so far I ended up completely confused just adding more layers from googled solutions and man pages.
I am not asking for a working code, just a comprehensible explanation which would help me to get through this problem.

glEnable(GL_TEXTURE_2D_ARRAY);
That gave you an OpenGL error. Even in compatibility profiles, you cannot enable an array texture of any kind. Why?
Because you cannot used fixed-function processing with array textures at all. You cannot use glTexEnv to fetch from an array texture. They're wholly and completely shader-based constructs.
So if you want to use an array texture, you must use a shader.

Related

How can I get at the raw pixel buffer of the window after drawing with OpenGL in SDL2?

This piece of SDL2 code draws some white pixels on-screen using OpenGL, then grabs the pixels field of the window's SDL_Surface and loops through it, printing out the values of the contents. Even though I just drew a white triangle, the loop shows that there's nothing but zeros in that buffer (the code just prints 0 to standard out over and over).
How can I actually get at the modified pixel buffer, in something like RGB or ARGB or RGBA format?
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <GL/glu.h>
int main()
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
const int WINDOW_WIDTH = 100;
const int WINDOW_HEIGHT = 100;
SDL_Window *window = SDL_CreateWindow("OpenGL Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLenum error = glGetError();
if( error != GL_NO_ERROR )
{
printf( "Error initializing OpenGL! %s\n", gluErrorString(error));
}
glClearColor(0, 0, 0, 1);
int quit = 0;
SDL_Event event;
while (!quit)
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
quit = 1;
break;
}
}
glBegin(GL_TRIANGLES);
glColor3f(255, 255, 255);
glVertex2f(0, 0);
glVertex2f(0, 1);
glVertex2f(1, 0);
glEnd();
SDL_GL_SwapWindow(window);
SDL_Surface *surface = SDL_GetWindowSurface(window);
int pixel_depth = SDL_BYTESPERPIXEL(surface->format->format);
char *pixels = (char*) surface->pixels;
int max_value = 0;
for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT * pixel_depth; i++)
{
if (pixels[i] > max_value)
{
max_value = pixels[i];
}
}
SDL_FreeSurface(surface);
SDL_Log("%d", max_value);
}
SDL_Quit();
return 0;
}
SDL_GetWindowSurface() doesn't work with OpenGL:
You may not combine this with 3D or the rendering API on this window.
Use glReadPixels() instead.
Use PBO to read data from from the pixel buffer.
glReadBuffer(GL_COLOR_ATTACHMENT0);
writeIndex = (writeIndex + 1) % 2;
readIndex = (readIndex + 1) % 2;
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[writeIndex]);
// copy from framebuffer to PBO asynchronously. it will be ready in the NEXT frame
glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// now read other PBO which should be already in CPU memory
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[readIndex]);
unsigned char* Data = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

From C matrix to texture in modern OpenGL?

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);

How to "palettetize" a 2D texture in modern OpenGL?

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);
// [...]
}

How can I set the "q" texture coordinate in openGL 3? [duplicate]

This question already has answers here:
Perspective correct texturing of trapezoid in OpenGL ES 2.0
(3 answers)
Closed 1 year ago.
I'm trying to apply a square texture to a trapezoid-like shape in OpenGL but I get some distortion. I've been reading a lot on possible solutions and the one that seems most convenient requires modifying the "q" texture coordinates. This is done using GlTexCoord functions in the solution; however, I'm using vertex buffers and I don't know how I can use them to change this coordinate this way. The texture init in GLSL takes a vec2; so I have no idea how I would pass anything but two-dimensional texture coordinates to it.
main.c
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//GL libs (Need to have set up to run this)
#include <glad/glad.h>
#include <GLFW/glfw3.h>
//Libs in folder
#include "shader.h"
#include "image.h"
//Window consts
#define WINDOW_HEIGHT 500
#define WINDOW_WIDTH 500
#define WINDOW_NAME "ForStackOverflow"
//Shader consts
#define VERTEX_SHADER_FILE "vertex_shader.glsl"
#define FRAGMENT_SHADER_FILE "fragment_shader.glsl"
//Vertex constants
#define POSITION_ATTRIBUTE_LOC 0
#define TEXTURE_COORD_ATTRIBUTE_LOC 1
#define POSITION_SIZE 2
#define TEXTURE_COORD_SIZE 2
#define VERTEX_SIZE (POSITION_SIZE + TEXTURE_COORD_SIZE) //Amount of floats per vertex
#define POSITION_OFFSET 0
#define TEXTURE_COORD_OFFSET (POSITION_SIZE * sizeof(float))
#define STRIDE (sizeof(float) * VERTEX_SIZE)
//Functions
static void framebuffer_size_callback(GLFWwindow*, int, int);
static unsigned int load_bmp_texture(const char* name);
int main()
{
printf("Running!\n");
//*GLFW
if (!glfwInit())
{
printf("GLFW init fail\n");
return -1;
}
//3.3 core
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//*Window object
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME, NULL, NULL);
if (window == NULL)
{
printf("GLFW window fail\n");
return -1;
}
glfwMakeContextCurrent(window);
//*GLAD
if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
{
printf("GLAD init fail");
glfwTerminate();
return -1;
}
//*Window
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
//*Shaders
ShaderProgram shader_program;
if (!shader_program_init(&shader_program, VERTEX_SHADER_FILE, FRAGMENT_SHADER_FILE)) {
glfwTerminate();
return -1;
}
//*Triangle rendering
//Vertices
float tri_vertices[4 * VERTEX_SIZE] = { //FORM A TRAPEZOID
//Position //Texture coordinates
-0.5f, 0.5f, 0.0f, 1.0f, //Top-left
-0.5f, -0.5f, 0.0f, 0.0f, //Bottom-left
0.5f, 0.75f, 1.0f, 1.0f, //Top-right
0.5f, -0.75f, 1.0f, 0.0f //Bottom-right
};
//Indices
unsigned int tri_indices[6] = {
2, 0, 1, //Top-right, top-left, bottom-left
2, 3, 1 //Top-right, bottom-right, bottom-left
};
//VAO
unsigned int tri_vao;
glGenVertexArrays(1, &tri_vao);
glBindVertexArray(tri_vao);
//VBO
unsigned int tri_vbo;
glGenBuffers(1, &tri_vbo);
glBindBuffer(GL_ARRAY_BUFFER, tri_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(tri_vertices), tri_vertices, GL_STATIC_DRAW);
//EBO
unsigned int tri_ebo;
glGenBuffers(1, &tri_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(tri_indices), tri_indices, GL_STATIC_DRAW);
//Config
//Position
glVertexAttribPointer(POSITION_ATTRIBUTE_LOC, POSITION_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) POSITION_OFFSET);
glEnableVertexAttribArray(POSITION_ATTRIBUTE_LOC);
//Texture coordinates
glVertexAttribPointer(TEXTURE_COORD_ATTRIBUTE_LOC, TEXTURE_COORD_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) TEXTURE_COORD_OFFSET);
glEnableVertexAttribArray(TEXTURE_COORD_ATTRIBUTE_LOC);
//*Textures
unsigned int brick_tex = load_bmp_texture("purple_bricks.bmp");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, brick_tex);
//Attaching to uniform
glUseProgram(shader_program);
glUniform1i(glGetUniformLocation(shader_program, "brick"), 0);
//*Rendering setup
//Shader
glUseProgram(shader_program);
//*Main loop
while (!glfwWindowShouldClose(window))
{
//*Blittering
//Background
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
//Triangles
glBindVertexArray(tri_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*) 0);
//*Buffer, events
glfwSwapBuffers(window);
glfwPollEvents();
}
//*End program
glDeleteVertexArrays(1, &tri_vao);
glDeleteBuffers(1, &tri_vbo);
glDeleteBuffers(1, &tri_ebo);
glfwTerminate();
return 0;
}
//Centers the OpenGL part of the window and keeps the same width and height
static void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport((width - WINDOW_WIDTH) / 2, (height - WINDOW_HEIGHT) / 2, WINDOW_WIDTH, WINDOW_HEIGHT);
}
//Loads and sets up a BMP texture
static unsigned int load_bmp_texture(const char* name) {
//Loading into array
RGBImage image;
read_bmp(name, &image);
//Generating texture in GL
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//Setting mapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //X
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //Y
//Setting filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Setting texture and mipmap
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data);
glGenerateMipmap(GL_TEXTURE_2D);
//Freeing image array
free_RGBImage(image);
return texture;
}
image.h
//Code for loading a bmp file as an array
//Definitely not part of the problem
//24 bit bmps
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
//Prevent struct packing
#pragma pack(1)
typedef struct RGB {
unsigned char R;
unsigned char G;
unsigned char B;
} RGB;
typedef struct RGBImage {
int width;
int height;
RGB* data;
} RGBImage;
typedef struct BMPFileHeader {
char name[2];
uint32_t size;
uint32_t garbage;
uint32_t image_offset;
} BMPFileHeader;
typedef struct BMPInfoHeader {
uint32_t header_size;
uint32_t width;
uint32_t height;
uint16_t color_planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
} BMPInfoHeader;
void free_RGBImage(RGBImage image) {
free(image.data);
}
bool read_bmp(const char* file_name, RGBImage* image) {
FILE* fp = fopen(file_name, "rb");
if (fp == NULL) {
printf("Couldn't open %s\n", file_name);
return false;
}
BMPFileHeader file_header;
fread(file_header.name, sizeof(BMPFileHeader), 1, fp);
if ((file_header.name[0] != 'B') || (file_header.name[1] != 'M')) {
fclose(fp);
return false;
}
BMPInfoHeader info_header;
fread(&info_header, sizeof(BMPInfoHeader), 1, fp);
if ((info_header.header_size != 40) || (info_header.compression != 0) || (info_header.bits_per_pixel != 24)) {
fclose(fp);
return false;
}
fseek(fp, file_header.image_offset, SEEK_SET);
image->width = info_header.width;
image->height = info_header.height;
image->data = (RGB*) malloc(image->height * image->width * sizeof(RGB));
for (int i = 0; i < image->height; i++) {
fread(&image->data[i * image->width], sizeof(RGB), image->width, fp);
}
int R;
for (int i = 0; i < image->height * image->width; i++) {
R = image->data[i].R;
image->data[i].R = image->data[i].B;
image->data[i].B = R;
}
fclose(fp);
return true;
}
shader.h
//Code for compiling and linking a shader
//Definitely not part of the problem
//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//GL libs
#include <glad/glad.h>
#include <GLFW/glfw3.h>
//Consts
#define INFO_LOG_SIZE 512
static bool _glad_is_init();
static bool _glfw_is_init();
typedef unsigned int ShaderProgram;
bool shader_program_init(ShaderProgram* shader_program, char* vertex_shader_file_name, char* fragment_shader_file_name)
{
if (!_glfw_is_init()) {
printf("Shader: glfw uninitialized\n");
return 0;
}
else if (!_glad_is_init()) {
printf("Shader: glad uninitialized\n");
return 0;
}
long int file_size;
size_t new_source_length;
FILE* fp;
//*Reading vertex shader file
//Open
fp = fopen(vertex_shader_file_name, "r");
if (fp == NULL) {
printf("Couldn't open vertex shader file\n");
return 0;
}
//Find length for buffer
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
if (file_size == -1) {
printf("Couldn't seek end of file\n");
return 0;
}
rewind(fp);
char vertex_shader_source[(file_size + 1) * sizeof(char)];
//Read
new_source_length = fread(vertex_shader_source, sizeof(char), file_size, fp);
if (ferror(fp) != 0) {
printf("Error when reading file\n");
return 0;
}
//Add string termination
vertex_shader_source[new_source_length] = '\0';
//Close
fclose(fp);
//*Reading fragment shader
//Open
fp = fopen(fragment_shader_file_name, "r");
if (fp == NULL) {
printf("Couldn't open fragment shader file\n");
return 0;
}
//Find length for buffer
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
if (file_size == -1) {
printf("Couldn't seek end of file\n");
return 0;
}
rewind(fp);
char fragment_shader_source[(file_size + 1) * sizeof(char)];
//Read
new_source_length = fread(fragment_shader_source, sizeof(char), file_size, fp);
if (ferror(fp) != 0) {
printf("Error reading file\n");
return 0;
}
//Add string termination
fragment_shader_source[new_source_length] = '\0';
//Close
fclose(fp);
//*Compiling
//For error checking
int success;
char infolog[INFO_LOG_SIZE];
const char* vertex_shader_code = vertex_shader_source; //vertex_shader_source is of type char foo[n], a VLA.
const char* fragment_shader_code = fragment_shader_source;
//Vertex
unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_code, NULL);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex_shader, INFO_LOG_SIZE, NULL, infolog);
printf("Vertex shader compile fail: \n%s\n", infolog);
return 0;
}
//Fragment
unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_code, NULL);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment_shader, INFO_LOG_SIZE, NULL, infolog);
printf("Fragment shader compile fail: \n%s\n", infolog);
return 0;
}
//Program
*shader_program = glCreateProgram();
glAttachShader(*shader_program, vertex_shader);
glAttachShader(*shader_program, fragment_shader);
glLinkProgram(*shader_program);
glGetProgramiv(*shader_program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(*shader_program, INFO_LOG_SIZE, NULL, infolog);
printf("Shader program compile fail: \n%s\n", infolog);
return 0;
}
//Deleting
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return 1;
}
bool _glfw_is_init() {
if (!glfwInit())
{
return false;
}
return true;
}
bool _glad_is_init() {
if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
{
return false;
}
return true;
}
vertex_shader.glsl
#version 330 core
layout (location = 0) in vec2 vertex_position;
layout (location = 1) in vec2 vertex_texture_coordinate;
out vec2 texture_coordinate;
void main()
{
gl_Position = vec4(vertex_position, 1.0, 1.0);
texture_coordinate = vertex_texture_coordinate;
};
fragment_shader.glsl
#version 330 core
in vec4 color;
in vec2 texture_coordinate;
out vec4 FragColor;
uniform sampler2D brick;
void main()
{
FragColor = texture(brick, texture_coordinate);
};
Texture used
Result of program
Edit: for those reading in the future, this link helped a lot with implementing the method described in the answer below:
https://www.cs.cmu.edu/~16385/s17/Slides/10.2_2D_Alignment__DLT.pdf
Given the 2d vertex coordinates P[i] and the 2d texture coordinates T[i] you need to find the homography that maps from T[i] to P[i]. The homography H is represented with a 3x3 matrix (up to a scaling factor) and can be calculated, for example, with the direct linear transform method. Beware that it involves solving a system of eight equations with eight/nine unknowns -- so it's best to resort to an existing library, like LAPACK, for that.
The homography satisfies the relations
H * T[i] ~ P[i]
as an equivalence on the projective plane. I.e.
H * (T[i], 1) = a[i] * (P[i], 1)
for some scaling factors a[i].
The trick to get the correct texture mapping is to replace the 2d vertex coordinates with 3d homogeneous ones:
homogeneous P[i] = H * (T[i], 1)
The vertex shader will then receive a vec3 vertex position, and copy the 3rd coordinate to gl_Position.w:
layout (location = 0) in vec3 vertex_position;
...
void main() {
gl_Position = vec4(vertex_position.xy, 0, vertex_position.z);
...
}
Here are the results I get with this method (corrected/uncorrected):

OpenGL C++ order for using a vertex buffer and a texture buffer

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.

Resources