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 want to change the content of a buffer that i share between multiple shaders, in the compute shader. Specifically the normals in this case.
No matter what i set the value of one or more vertices in the buffer to, it doesn't seem to take effect in any of the other shaders. Probably i just forgot something or am not seeing something (I am new to compute shaders)
Here is the relevant part of the code. If you need more parts of the code, just ask. Please understand though that its hard to give a working piece of code because that requires quiet a bit more code than just a few lines.
utils_createComputeProgram(&g_simulateWaterProgram, "content/shaders/simulatewater/simulatewater.comp");
// Initialize first position buffer
{
glGenBuffers(1, &app->positionBufferOne);
glBindBuffer(GL_ARRAY_BUFFER, app->positionBufferOne);
glBufferData(GL_ARRAY_BUFFER, NUM_VERTICES*sizeof(MLvec4), NULL, GL_STATIC_DRAW);
MLvec4* positions = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
for (int i = 0; i < NUM_VERTICES; ++i) {
...
positions[i] = mlMakeVec4(x, y, z, v);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Initialize second position buffer with 0
{
glGenBuffers(1, &app->positionBufferTwo);
glBindBuffer(GL_ARRAY_BUFFER, app->positionBufferTwo);
glBufferData(GL_ARRAY_BUFFER, NUM_VERTICES*sizeof(MLvec4), NULL, GL_STATIC_DRAW);
MLvec4* positions = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
for (int i = 0; i < NUM_VERTICES; ++i) {
positions[i] = mlMakeVec4(0, 0, 0, 0);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// initialize normal buffer
{
glGenBuffers(1, &app->normalBuffer);
glBindBuffer(GL_ARRAY_BUFFER, app->normalBuffer);
glBufferData(GL_ARRAY_BUFFER, NUM_VERTICES*sizeof(MLvec4), NULL, GL_STATIC_DRAW);
MLvec4* normals = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
for (int i = 0; i < NUM_VERTICES; ++i) {
normals[i] = mlMakeVec4(0, 0, 1, 0);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Attach buffers to program
{
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, app->positionBufferOne);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, app->positionBufferTwo);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, app->normalBuffer);
}
utils_createProgramVertexFragment(&g_renderWaterProgram, "content/shaders/renderwater/renderwater.vert", "content/shaders/renderwater/renderwater.frag");
// initialize VAO
{
glGenVertexArrays(1, &g_waterVertexArrayObject);
utils_setAttributePointer(g_waterVertexArrayObject, app->positionBufferOne, 0, 4, GL_FLOAT, GL_FALSE, 0, 0);
utils_setAttributePointer(g_waterVertexArrayObject, app->positionBufferTwo, 1, 4, GL_FLOAT, GL_FALSE, 0, 0);
utils_setAttributePointer(g_waterVertexArrayObject, app->normalBuffer, 2, 4, GL_FLOAT, GL_FALSE, 0, 0);
}
// initialize index array
{
...
glBindVertexArray(g_waterVertexArrayObject);
glGenBuffers(1, &g_elementArrayBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_elementArrayBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * NUM_INDICES, g_waterIndices, GL_STATIC_DRAW);
}
void rendering_render(Application* app, int framebufferWidth, int framebufferHeight) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, framebufferWidth, framebufferHeight);
MLvec3 center = mlMakeVec3(app->camera.cx, app->camera.cy, app->camera.cz);
MLvec3 eye = positionOfCamera(app->camera);
MLmat4 modelMatrix = mlIdentity4;
MLmat4 projectionMatrix = mlMakePerspectiveFromFOV(100, (float)framebufferWidth / framebufferHeight, 0.01, 100.0);
MLmat4 viewMatrix = mlMakeLookAt(eye, center, mlMakeVec3(0, 1, 0));
// Hier muss gewartet werden, bis der Compute Shader alle Schreiboperationen auf die Shader Storage Buffer ausgeführt hat.
//glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
glUseProgram(g_renderWaterProgram);
utils_setUniformMat4(renderWaterProgram_ProjectionMatrix, &projectionMatrix);
utils_setUniformMat4(renderWaterProgram_ViewMatrix, &viewMatrix);
utils_setUniformMat4(renderWaterProgram_ModelMatrix, &modelMatrix);
if (app->debug) {
glDrawElements(GL_POINTS, NUM_INDICES, GL_UNSIGNED_INT, NULL);
}
else {
glDrawElements(GL_TRIANGLES, NUM_INDICES, GL_UNSIGNED_INT, NULL);
}
glUseProgram(0);
}
Compute shader:
layout (local_size_x = 1000, local_size_y = 1, local_size_z = 1) in;
layout (location = 0) uniform float Dt;
layout (std430, binding = 0) buffer PositionBufferOne {
vec4 positions[];
};
layout (std430, binding = 1) buffer PositionBufferTwo {
vec4 positionsNew[];
};
layout (std430, binding = 2) buffer NormalBuffer {
vec4 normals[];
};
vec3 calcNormal() {
return normalize(vec3(1, 1, 1));
}
void main() {
uint index = gl_GlobalInvocationID.x;
normals[index] = vec4(calcNormal(), 0.0);
}
utils:
GLuint utils_createShader(GLenum shaderType, const char* filename) {
GLuint shader = glCreateShader(shaderType);
const char* sources[2];
sources[0] = common_readfile("content/shaders/utils.glsl");
sources[1] = common_readfile(filename);
glShaderSource(shader, 2, sources, NULL);
free((void*)sources[0]);
free((void*)sources[1]);
glCompileShader(shader);
utils_checkShaderLog(filename, shader);
return shader;
}
void utils_createComputeProgram(GLuint *program, const char* computeShaderFilename) {
glDeleteProgram(*program);
GLuint computeShader = utils_createShader(GL_COMPUTE_SHADER, computeShaderFilename);
*program = glCreateProgram();
glAttachShader(*program, computeShader);
glLinkProgram(*program);
utils_checkProgramLog(*program);
glDeleteShader(computeShader);
}
void utils_setAttributePointer(GLuint vertexArrayObject, GLuint buffer, GLint location, GLint size, GLenum type, GLboolean normalized, GLsizei stride, unsigned offset) {
GLint previousVertexArrayObject;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &previousVertexArrayObject);
GLint previousArrayBuffer;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &previousArrayBuffer);
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glEnableVertexAttribArray(location);
glVertexAttribPointer(location, size, type, normalized, stride, (void*)(intptr_t)offset);
glBindBuffer(GL_ARRAY_BUFFER, previousArrayBuffer);
glBindVertexArray(previousVertexArrayObject);
}
void utils_setUniformMat4(GLuint location, MLmat4* m) {
glUniformMatrix4fv(location, 1, GL_FALSE, (GLfloat*)m);
}
I tested changes by using the normals as the colors for my fragments.
In this example i set the normals to 0/0/0 during initialization and in the compute shader i change the value of every normal to normalized 1/1/1 thus expect so see the geometry in somewhat of a greyish color, but it stays black.
// EDIT
After starting from scratch and testing the result basically after every row of code i added i found out the problem was this line: glDispatchCompute(NUM_VERTICES / NUM_PARTICLES_PER_LOCAL_WORK_GROUP, 1, 1); i guess this evaluated to zero, which kind of makes sense with a "low" number of vertices and relatively high number of particles per work group.
That resulted in whatever i did in the compute shader was never actually being executed thus, not changing the buffer content.
It's been a while since I've wrote some graphics programming and I've been staring at this for quite some time now and can't figure out what i'm doing wrong.
This is how I am creating my texture.
GLuint create_texture(const char* filename) {
SDL_Surface* surface;
GLenum tex_format;
GLint num_colors;
GLuint tex_id;
char* file_path;
file_path = get_resource(filename);
surface = IMG_Load(file_path);
if (!surface) {
SDL_Log("failed to create surface\n");
SDL_Quit();
return -1;
} else {
if ((surface->w & (surface->w - 1)) != 0) {
SDL_Log("image { %s } width is not power of 2\n", filename);
}
if ((surface->h & (surface->h - 1)) != 0) {
SDL_Log("image { %s } height is not power of 2\n", filename);
}
num_colors = surface->format->BytesPerPixel;
if (num_colors == 4) {
if (surface->format->Rmask == 0x000000ff)
tex_format = GL_RGBA;
else
tex_format = GL_BGRA;
}
if (num_colors == 3) {
if (surface->format->Rmask == 0x000000ff)
tex_format = GL_RGB;
else
tex_format = GL_BGR;
} else {
SDL_Log("pixel image format shouldn't get here! Quitting\n");
SDL_Quit();
};
glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, tex_format, surface->w, surface->h, 0,
tex_format, GL_UNSIGNED_BYTE, surface->pixels);
}
if (surface) SDL_FreeSurface(surface);
free(file_path);
return tex_id;
}
this code above works correctly for loading the image data.
vertex shader
#version 330 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec4 a_col;
layout (location = 2) in vec2 a_tex;
uniform mat4 u_mvp_mat;
uniform mat4 u_mod_mat;
uniform mat4 u_view_mat;
uniform mat4 u_proj_mat;
out vec4 f_color;
out vec2 f_tex;
void main()
{
gl_Position = u_mvp_mat * vec4(a_pos, 1.0);
f_tex = a_tex;
f_color = a_col;
}
fragment shader.
#version 330 core
in vec4 f_color;
out vec4 o_color;
in vec2 f_tex;
uniform sampler2D u_sprite_tex;
void main (void)
{
o_color = f_color;
o_color = texture(u_sprite_tex, f_tex);
}
this is how i setup my VBO
quad = ren2d_new(); //just a wrapper around a quad or two tri.
ren2d_set_tint(quad, 0, 1, 0, 1); //setting the color to green
pos_loc = get_attrib_location(ce_get_default_shader(), "a_pos");
col_loc = get_attrib_location(ce_get_default_shader(), "a_col");
mvp_matrix_loc = get_uniform_location(ce_get_default_shader(), "u_mvp_mat");
model_mat_loc = get_uniform_location(ce_get_default_shader(), "u_mod_mat");
view_mat_loc = get_uniform_location(ce_get_default_shader(), "u_view_mat");
proj_matrix_loc =
get_uniform_location(ce_get_default_shader(), "u_proj_mat");
tex_loc = get_uniform_location(ce_get_default_shader(), "u_sprite_tex");
camera = cam_2d_new(ce_get_width(), ce_get_height());
model_mat = mat4_identity();
mat4_scale(model_mat, 128, 128, 1);
tex_id = create_texture("test.png");
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vert_buff);
glBindBuffer(GL_ARRAY_BUFFER, vert_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->vertices), quad->vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &ind_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind_buff);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad->indices), quad->indices,
GL_STATIC_DRAW);
glBindVertexArray(0);
this is my render function:
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//could my texturing problems be here?
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(tex_loc, 0);
glUseProgram(ce_get_default_shader()->shader_program);
glBindVertexArray(vao);
//excuse the silly names
ce_get_view_matrices(&vview_mat, &pproj_mat, &mmvp_mat);
mat4_multi(&mmvp_mat, &vview_mat, model_mat);
mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(&mmvp_mat));
glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat));
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(&vview_mat));
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(&pproj_mat));
glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
I am just getting a black quad. I can't track it down and i've been staring at this for quite a few hours now.
doing some very basic testing in the fragment shader like this.
#version 330 core
in vec4 f_color;
out vec4 o_color;
in vec2 f_tex;
uniform sampler2D u_sprite_tex;
void main (void)
{
//o_color = f_color;
vec4 c;
if(f_tex.y == 1.0) { c = vec4(1.0, 0.0, 0.0, 1.0); }
if(f_tex.y == 0.0) { c = vec4(0.0, 0.0, 1.0, 1.0); }
o_color = c;
o_color = texture(u_sprite_tex, f_tex);
}
my quads turn out to be blue both along the x and the y axis. That seems to be that the only value being passed in in 0.0 which doesn't make sense.
okay this was definitely a problem of my own creation. The reason that I was getting segmentation fault was because of this code.
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vert_buff);
glBindBuffer(GL_ARRAY_BUFFER, vert_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->vertices), quad->vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0); //right here
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
glEnableVertexAttribArray(2);
glGenBuffers(1, &ind_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind_buff);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quad->indices), quad->indices,
GL_STATIC_DRAW);
glBindVertexArray(0);
after you bind your vbo buffer to zero attempting to access it will cause a segmentation fault...
After that just making sure that I sent the proper data to the gpu and all other code worked as expected.
GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad->colors), quad->colors,
GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
//glBindBuffer(GL_ARRAY_BUFFER, 0); //right here remove this
//properly setup the local data to gpu data
glBufferData(GL_ARRAY_BUFFER, rend2d_get_sizeof_tex_coord(quad),
rend2d_get_tex_coord_data(quad), GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0); //put it here after finished with vbo
after those two changes everything worked as expected. Hopefully this can save someone some time.
There are at least two things that might be wrong:
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(quad->tex_coords),
quad->tex_coords);
Are you sure that quad->tex_coords contains 4-dimensional vectors? The fragment shader expects vec2. If you only supply two floats per vertex, you have to change the second parameter to two. When OpenGL tries to read from a too small ARRAY_BUFFER this can also cause segmentation faults. Note, that attributes are set to inactive if they do not contribute to the final color, thus the texture coordinates might only be read when textures are used.
Second: You are calling glGenerateMipmap before uploading data to the texture.
I'm trying to render a white square in OpenGL and I have this function to do so:
void main_loop(window_data *window)
{
printf("%s\n", glGetString(GL_VERSION));
GLuint vertex_array_objects, vertex_buffer_objects;
glGenVertexArrays(1, &vertex_array_objects);
glBindVertexArray(vertex_array_objects);
glGenBuffers(1, &vertex_buffer_objects);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_objects);
GLfloat vertices[6][2] = {
{-0.90, -0.90},
{0.85, -0.90},
{-0.90, 0.85},
{0.90, -0.85},
{0.90, 0.90},
{-0.85, 0.90}};
glBufferData(
GL_ARRAY_BUFFER,
sizeof(vertices),
vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(0,
2,
GL_FLOAT,
GL_FALSE,
0,
(void*)0);
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window->glfw_window))
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vertex_array_objects);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window->glfw_window);
glfwPollEvents();
}
return;
}
Which works perfectly fine, but then I tried to split this up into two functions like so:
void initialize_image(GLuint *vArray, GLuint *vBuffer, GLfloat vertices[][2])
{
glGenVertexArrays(1, vArray);
glBindVertexArray(*vArray);
glGenBuffers(1, vBuffer);
glBindBuffer(GL_ARRAY_BUFFER, *vBuffer);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(vertices),
vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(
0,
2,
GL_FLOAT,
GL_FALSE,
0,
(void*)0);
glEnableVertexAttribArray(0);
return;
}
And then I call it in the main_loop function (just before the while loop):
initialize_image(&vArray, &vBuffer, vertices);
But that keeps giving me a black screen of nothingness.
What could be causing this?
When you pass pointer to vertices array its sizeof() is the pointer size and not your data size!
Pass additional vertices size argument to your function and use it instea d of sizeof for glBufferData call.
Im trying to render a non power of 2 texture using a model class that creates a texture and stores it's ID and size (thanks to ADC and another stackoverflow member), and additional code the create VBO's and render it (The code for VBO's and rendering is for testing purposes, it is properly divided in my models).
When I use this code, but remove the texture-specific code, it works fine. When adding the texture specific code however, it only renders what I believe to be the bottom-right pixel, from trying out multiple complex images and using DigitalColor Meter to check the images and output.
I don't suppose that texture coordinates have to be specified explicitly when using this setup, and if they do, how would I?
Main OpenGL Code
// Create vertex buffer
GLuint memoryPointer = 0;
GLuint colourMemoryPointer = 0;
GLfloat *vertices;
size_t vertex_size = 0;
int check = AllocateVertexBuffer(2, 4, &vertices, &vertex_size);
CDMeshVertexesCreateRectangle(200, 200, vertices);
// Create colour buffer
GLfloat *colors;
size_t color_size;
int check2 = AllocateVertexBuffer(4, 4, &colors, &color_size);
CDMeshColorsCreateGrey(1.0, 4, colors);
// Create texture buffer
CDTexture *texture = [CDTexture loadPngTexture:#"Rawr"];
// Allocate the buffer
glGenBuffers(1, &memoryPointer);
// Bind the buffer object (tell OpenGL what to use)
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
// Allocate space for the VBO
glBufferData(GL_ARRAY_BUFFER, vertex_size, vertices, GL_STATIC_DRAW);
// Allocate the buffer
glGenBuffers(1, &colourMemoryPointer);
// Bind the buffer object (tell OpenGL what to use)
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
// Allocate space for the VBO
glBufferData(GL_ARRAY_BUFFER, color_size, colors, GL_STATIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY); // Activate vertex coordinates array
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture.ID);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
//[texture render];
//render
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY); // Deactivate vertex coordinates array
glDisableClientState(GL_COLOR_ARRAY);
free(vertices);
free(colors);
CDTexture loadPngTexture: Method
CFURLRef textureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
(CFStringRef)fileName,
CFSTR("png"),
NULL);
NSAssert(textureURL, #"Texture name invalid");
// Get the image source using a file path
CGImageSourceRef myImageSourceRef = CGImageSourceCreateWithURL(textureURL, NULL);
NSAssert(myImageSourceRef, #"Invalid Image Path.");
NSAssert((CGImageSourceGetCount(myImageSourceRef) > 0), #"No Image in Image Source.");
// Get the image reference using the source reference, -_-
CGImageRef myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL);
NSAssert(myImageSourceRef, #"Image not created.");
// Start gathering data from the image, before releasing it
GLuint myTextureName;
size_t width = CGImageGetWidth(myImageRef);
size_t height = CGImageGetHeight(myImageRef);
CGRect rect = {{0, 0}, {width, height}};
void * myData = calloc(width * 4, height);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef myBitmapContext = CGBitmapContextCreate (myData,
width, height, 8,
width*4, space,
kCGBitmapByteOrder32Host |
kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
// Flip so that it isn't upside-down
CGContextTranslateCTM(myBitmapContext, 0, height);
CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f);
CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy);
CGContextDrawImage(myBitmapContext, rect, myImageRef);
CGContextRelease(myBitmapContext);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
// Generate texture buffer
glGenTextures(1, &myTextureName);
// Bind buffer for use
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName);
// Set storage methods
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Set the parameter required for non power of two textures
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Load the texture data into the buffer
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, width, height,
0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, myData);
// Free the data used, as it's now in the buffer
free(myData);
// Return information on texture object
CDTexture *texture = [[CDTexture alloc] init];
texture.ID = myTextureName;
texture.size = NSMakeSize(width, height);
return texture;
UPDATE
I've tried using a texture pointer to define the area, and this results in the same problem. The colour/texture it draws will do so even without binding and using a pointer. I have also tried enabling and disabling GL_TEXTURE_COORD_ARRAY before and after it is used and rendered, and results in the same problem.
Unless i've done something wrong, the problem doesn't seem related with texture pointers.
GLfloat texCoords[8] = {
0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0
};
...
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture.ID);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
//[texture render];
//render
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
UPDATE 2
I've tried obtaining an error code from OpenGL to try and solve the problem, but no error codes have been printed.
UPDATE 3
I have tried using a different method to get raw image data, and the same result happens (albeit with some discolouration, as I must not be using the right colour settings). OpenGL's interpretation of the data must be flawed here. I have experimented with glPixelStorei() but nothing has changed so far. As OpenGL isn't reporting any error, it must be with the programs interpretation of the data, so either the parameters I have set for how data is stored, or vertex problems that is causing only 1 pixel to be displayed, although because of this precision, it is most likely to be parameters.
New process for raw image data (thanks to another stackoverflow user). Posted for reference.
NSBitmapImageRep *theImage;
int bitsPPixel, bytesPRow;
NSSize size;
unsigned char *theImageData;
NSData* imgData = [NSData dataWithContentsOfFile:fileName options:NSUncachedRead error:nil]; // use FileURL
theImage = [NSBitmapImageRep imageRepWithData:imgData];
if( theImage != nil )
{
bitsPPixel = [theImage bitsPerPixel];
bytesPRow = [theImage bytesPerRow];
size.width = [theImage pixelsWide];
size.height = [theImage pixelsHigh];
}
enter code here
UPDATE 4
Changed to GL_TEXTURE_2D for efficiency. The bottom-left pixel is still only being rendered. Here is the full order of the code:
// Create vertex buffer
GLuint memoryPointer = 0;
GLuint colourMemoryPointer = 0;
GLfloat *vertices;
size_t vertex_size = 0;
AllocateVertexBuffer(2, 4, &vertices, &vertex_size);
CDMeshVertexesCreateRectangle(200, 200, vertices);
// Create colour buffer
GLfloat *colors;
size_t color_size;
AllocateVertexBuffer(4, 4, &colors, &color_size);
CDMeshColorsCreateGrey(1.0, 4, colors);
// Create Texture UV Coordinates
GLfloat *texture;
size_t texture_size;
AllocateVertexBuffer(4, 2, &texture, &texture_size);
CDMeshVertexesCreateRectangle(1, 1, texture);
// Create texture buffer
NSString *fileName = #"Rawr3";
CFURLRef textureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
(CFStringRef)fileName,
CFSTR("png"),
NULL);
NSAssert(textureURL, #"Texture name invalid");
// Get the image source using a file path
CGImageSourceRef myImageSourceRef = CGImageSourceCreateWithURL(textureURL, NULL);
NSAssert(myImageSourceRef, #"Invalid Image Path.");
NSAssert((CGImageSourceGetCount(myImageSourceRef) > 0), #"No Image in Image Source.");
// Get the image reference using the source reference, -_-
CGImageRef myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL);
NSAssert(myImageSourceRef, #"Image not created.");
// Start gathering data from the image, before releasing it
GLuint myTextureName;
size_t width = CGImageGetWidth(myImageRef);
size_t height = CGImageGetHeight(myImageRef);
CGRect rect = {{0, 0}, {width, height}}; //Doesnt need fiddling
void * myData = calloc(width * 4, height); //Fiddled
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef myBitmapContext = CGBitmapContextCreate (myData,
width, height, 8,
width*4, space,
kCGBitmapByteOrder32Host |
kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
// Flip so that it isn't upside-down
CGContextTranslateCTM(myBitmapContext, 0, height);
CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f);
CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy);
CGContextDrawImage(myBitmapContext, rect, myImageRef);
CGContextRelease(myBitmapContext);
// Generate texture buffer
glGenTextures(1, &myTextureName);
// Bind buffer for use
glBindTexture(GL_TEXTURE_2D, myTextureName);
// Set storage methods
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_STORAGE_HINT_APPLE,
GL_STORAGE_CACHED_APPLE);
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
// Set clamping and rendering preferences
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
// Load the texture data into the buffer
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height,
0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, myData);
glGetError();
// Free the data used, as it's now in the buffer
free(myData);
// Return information on texture object
CDTexture *textureObj = [[CDTexture alloc] init];
textureObj.ID = myTextureName;
textureObj.size = NSMakeSize(width, height);
// Allocate the buffer
glGenBuffers(1, &memoryPointer);
// Bind the buffer object (tell OpenGL what to use)
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
// Allocate space for the VBO
glBufferData(GL_ARRAY_BUFFER, vertex_size, vertices, GL_STATIC_DRAW);
// Allocate the buffer
glGenBuffers(1, &colourMemoryPointer);
// Bind the buffer object (tell OpenGL what to use)
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
// Allocate space for the VBO
glBufferData(GL_ARRAY_BUFFER, color_size, colors, GL_STATIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY); // Activate vertex coordinates array
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindTexture(GL_TEXTURE_2D, textureObj.ID);
glTexCoordPointer(2, GL_FLOAT, 0, texture);
GetGLError();
//render
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY); // Deactivate vertex coordinates array
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glDeleteBuffers(1, &memoryPointer);
glDeleteBuffers(1, &colourMemoryPointer);
GLuint texID = textureObj.ID;
glDeleteBuffers(1, &texID);
free(vertices);
free(colors);
free(texture);
}
UPDATE 5
From glTexImage2D(), it will render the bottom-left pixel of the texture even if I don't bind the texture and add coordinates for it, which seems strange, considering that it should be drawn using glTexCoordPointer().
UPDATE 6
Probably the last update, used the texture initialisation code with another more, 'manual' technique of drawing and it worked fine. I have removed the Apple specific elements. When removing the colour VBO and pointer from this code, a white box is still rendered for some odd reason. The problem is with the VBO's, but I cant see why. GL_TEXTURE_RECTANGLE_ARB is just being used for my coloured texture right now, will be changed when this problem is solved.
// Create vertex buffer
GLuint memoryPointer = 0;
GLuint colourMemoryPointer = 0;
GLfloat *vertices;
size_t vertex_size = 0;
AllocateVertexBuffer(2, 4, &vertices, &vertex_size);
CDMeshVertexesCreateRectangle(200, 200, vertices);
GetGLError();
// Create colour buffer
GLfloat *colors;
size_t color_size;
AllocateVertexBuffer(4, 4, &colors, &color_size);
CDMeshColorsCreateGrey(0.4, 4, colors);
// Allocate the buffer
glGenBuffers(1, &memoryPointer);
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glBufferData(GL_ARRAY_BUFFER, vertex_size, vertices, GL_STATIC_DRAW);
// Allocate the buffer
glGenBuffers(1, &colourMemoryPointer);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glBufferData(GL_ARRAY_BUFFER, color_size, colors, GL_STATIC_DRAW);
// Enable client states for drawing the various arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Bind each buffer and use the VBO's to draw (apart from the texture buffer)
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName); //
glTexCoordPointer(2, GL_FLOAT, 0, texture);
//render
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// Disale client states as were done with them.
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_RECTANGLE_ARB);
GetGLError();
// Delete buffers to avoid problems
glDeleteBuffers(1, &memoryPointer);
glDeleteBuffers(1, &colourMemoryPointer);
glDeleteBuffers(1, &myTextureName);
free(vertices);
free(colors);
free(texture);
Of course you must supply texture coordinates. Either as part of the vertex attribute vector, or by generating them (in a shader). But you must supply texture coordinates somehow, you can't get around this.
I solved the problem (finally) by creating a Vertex Buffer Object (VBO) for the texture, instead of trying to supply vertex data directly to glTextureCoordPointer(). I guess the lesson here is that if you are going to use VBO's, use them for everything! For reference, here is the complete code:
Texture Creation
// Create texture buffer, start with the name of the image.
NSString *fileName = #"Rawr";
CFURLRef textureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
(CFStringRef)fileName,
CFSTR("png"),
NULL);
NSAssert(textureURL, #"Texture name invalid");
// Get the image source using a file path
CGImageSourceRef myImageSourceRef = CGImageSourceCreateWithURL(textureURL, NULL);
NSAssert(myImageSourceRef, #"Invalid Image Path.");
NSAssert((CGImageSourceGetCount(myImageSourceRef) > 0), #"No Image in Image Source.");
// Get the image reference using the source reference, -_-
CGImageRef myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL);
NSAssert(myImageSourceRef, #"Image not created.");
// Start gathering data from the image, before releasing it
GLuint myTextureName;
size_t width = CGImageGetWidth(myImageRef);
size_t height = CGImageGetHeight(myImageRef);
CGRect rect = {{0, 0}, {width, height}}; //Doesnt need fiddling
void * myData = calloc(width * 4, height); //Fiddled
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef myBitmapContext = CGBitmapContextCreate (myData,
width, height, 8,
width*4, space,
kCGBitmapByteOrder32Host |
kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
// Flip so that it isn't upside-down
CGContextTranslateCTM(myBitmapContext, 0, height);
CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f);
CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy);
CGContextDrawImage(myBitmapContext, rect, myImageRef);
CGContextRelease(myBitmapContext);
GetGLError();
// The extension GL_TEXTURE_RECTANGLE_ARB can be used for non-power of two textures, but it is slower than GL_TEXTURE_2D and not
// supported by all graphics cards.
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glGenTextures(1, &myTextureName);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, myData);
//glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT ,GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,myTextureName, 0);
free(myData);
VBO Creation
// Create vertex buffer
GLuint memoryPointer = 0;
GLuint colourMemoryPointer = 0;
GLuint texPointer = 0;
GLfloat *vertices;
size_t vertex_size = 0;
AllocateVertexBuffer(2, 4, &vertices, &vertex_size);
CDMeshVertexesCreateRectangle(200, 200, vertices);
GetGLError();
// Create colour buffer
GLfloat *colors;
size_t color_size;
AllocateVertexBuffer(4, 4, &colors, &color_size);
CDMeshColorsCreateGrey(0.0, 4, colors);
// Create Texture UV Coordinates
GLfloat *texture;
size_t texture_size;
AllocateVertexBuffer(2, 4, &texture, &texture_size);
CDMeshVertexesCreateRectangle(200, 200, texture);
// Allocate the vertex VBO
glGenBuffers(1, &memoryPointer);
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glBufferData(GL_ARRAY_BUFFER, vertex_size, vertices, GL_STATIC_DRAW);
// Allocate the colour VBO
glGenBuffers(1, &colourMemoryPointer);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glBufferData(GL_ARRAY_BUFFER, color_size, colors, GL_STATIC_DRAW);
// Allocate the texture VBO
glGenBuffers(1, &texPointer);
glBindBuffer(GL_ARRAY_BUFFER, texPointer);
glBufferData(GL_ARRAY_BUFFER, texture_size, texture, GL_STATIC_DRAW);
Rendering and Deleting VBO and Data
// Enable client states for drawing the various arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Bind each buffer and use the VBO's to draw
glBindBuffer(GL_ARRAY_BUFFER, memoryPointer);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colourMemoryPointer);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, texPointer);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName);
// RENDER!
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// Disale client states as were done with them.
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_RECTANGLE_ARB);
GetGLError();
// Delete buffers when they arent being used.
glDeleteBuffers(1, &memoryPointer);
glDeleteBuffers(1, &colourMemoryPointer);
glDeleteBuffers(1, &myTextureName);
glGenTextures(GL_TEXTURE_RECTANGLE_ARB, 0);
// Free vertexes when done using them
free(vertices);
free(colors);
free(texture);