c++openglaccess-violationtexturing

Access violation when trying to map a texture to a triangle in GLSL


The issue lies in when I pass the texture I want to draw to the fragment shader, its a 64x64 RGBA unsigned 8-bit texture. I have looked at code from other people, read function definitions. Checked for errors using glGetError() which returns 0 regardless where i put it in the program until it throws the access violation after calling glDrawArrays(GL_TRIANGLES, 0, 6); It's probably something super obvious and i recently asked a question, but I've been trying so hard to solve this on my own. So I hope I'm not going to feel bothersome asking this question.

This is the code where I try to load my texture into memory

glGenTextures(1, textures);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, BTH_IMAGE_WIDTH, BTH_IMAGE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, BTH_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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

This is where I try to pass it to the shader

GLint texture0 = glGetUniformLocation(gShaderProgram, "texture0");
glUniform1i(texture0, 0);

My fragment shader

#version 400

out vec4 fragment_color;

uniform sampler2D texture0;

in fData
{
    vec3 normal;
    vec4 color;
    mat4 v;
    vec2 tex_coord;
} frag;



void main () {
    vec3 n = normalize(frag.normal);
    float intensity = min(max(dot(n, vec3(0,0,-1)), 0.0), 1.0);

    //fragment_color = frag.color * (intensity);
    fragment_color = texture(texture0,frag.tex_coord.st) *  intensity;
}

full main.cpp

//--------------------------------------------------------------------------------------
// BTH - Stefan Petersson 2014.
//--------------------------------------------------------------------------------------
#include <vector>
#include <windows.h>
#include <iostream>
#include <string>
#include <fstream>
#include <streambuf>
#include <chrono>
#include <gl/glew.h>
#include <gl/GL.h>
# define M_PI           3.14159265358979323846
#include "glm\glm.hpp"
#include "glm\gtc\matrix_transform.hpp"
#include "bth_image.h"
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glew32.lib")

using namespace std;
using namespace glm;
HWND InitWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HGLRC CreateOpenGLContext(HWND wndHandle);

GLuint gVertexBuffer = 0;
GLuint gVertexAttribute = 0;
GLuint gShaderProgram = 0;
GLuint textures[1];
mat4x4 view;
mat4x4 world;
mat4x4 projection;
float DT;

struct CPUvalues
{
    float v1;
    float v2;
    float v3;
    float v4;
};

CPUvalues Gv = { 0.5, 0, 0, 0 };

GLuint gu = 0;

#define BUFFER_OFFSET(i) ((char *)nullptr + (i))

void CreateShaders()
{
    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    ifstream shaderFile("VertexShader.glsl");
    std::string shaderText((std::istreambuf_iterator<char>(shaderFile)), std::istreambuf_iterator<char>());
    shaderFile.close();
    const char* shaderTextPtr = shaderText.c_str();
    glShaderSource(vs, 1, &shaderTextPtr, nullptr);
    glCompileShader(vs);

    //create fragment shader | same process.
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    shaderFile.open("Fragment.glsl");
    shaderText.assign((std::istreambuf_iterator<char>(shaderFile)), std::istreambuf_iterator<char>());
    shaderFile.close();
    shaderTextPtr = shaderText.c_str();
    glShaderSource(fs, 1, &shaderTextPtr, nullptr);
    glCompileShader(fs);

    GLuint gs = glCreateShader(GL_GEOMETRY_SHADER);
    shaderFile.open("GMshader.glsl");
    shaderText.assign((std::istreambuf_iterator<char>(shaderFile)), std::istreambuf_iterator<char>());
    shaderFile.close();
    shaderTextPtr = shaderText.c_str();
    glShaderSource(gs, 1, &shaderTextPtr, nullptr);
    glCompileShader(gs);

    GLint success = 0;
    glGetShaderiv(gs, GL_COMPILE_STATUS, &success);
    if (success == GL_FALSE)
    {
        GLint logSize = 0;
        glGetShaderiv(gs, GL_INFO_LOG_LENGTH, &logSize);
        std::vector<GLchar> errorLog(logSize);
        glGetShaderInfoLog(gs, logSize, &logSize, &errorLog[0]);
        for (int i = 0; i < errorLog.size(); i++)
        {
            cout << errorLog.at(i);
        }
    }
    //link shader program (connect vs and ps)
    gShaderProgram = glCreateProgram();
    glAttachShader(gShaderProgram, fs);
    glAttachShader(gShaderProgram, gs);
    glAttachShader(gShaderProgram, vs);
    glLinkProgram(gShaderProgram);


    glGenTextures(1, textures);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, BTH_IMAGE_WIDTH, BTH_IMAGE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, BTH_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_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);





    GLint isLinked = 0;
    glGetProgramiv(gShaderProgram, GL_LINK_STATUS, &isLinked);
    if (isLinked == GL_FALSE)
    {
        GLint maxLength = 0;
        glGetProgramiv(gShaderProgram, GL_INFO_LOG_LENGTH, &maxLength);
        std::vector<GLchar> infoLog(maxLength);
        glGetProgramInfoLog(gShaderProgram, maxLength, &maxLength, &infoLog[0]);
        for (GLint i = 0; i < maxLength; i++)
        {
            cout << infoLog.at(i);
        }
    }
}

void CreateTriangleData()
{
    // this is how we will structure the input data for the vertex shader
    // every six floats, is one vertex.

    struct TriangleVertex
    {
        float x, y, z;
        float r, g, b;
        float s, t;
    };
    // create the actual data in plane Z = 0
    TriangleVertex triangleVertices[6] = 
    {
        // pos and color for each vertex
        { -0.5f, 0.5f, 0.0f,    1.0f, 0.0f, 0.0f,   1.0f, 0.0f },
        { 0.5f, -0.5f, 0.0f,    0.0f, 1.0f, 0.0f,   0.0f, 1.0f },
        { -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   1.0f, 0.0f },

        { 0.5f, 0.5f, 0.0f,     0.0f, 0.0f, 1.0f,   1.0f, 0.0f },
        { 0.5f, -0.5f, 0.0f,    0.0f, 1.0f, 0.0f,   0.0f, 1.0f },
        { -0.5f, 0.5f, 0.0f,    1.0f, 0.0f, 0.0f,   1.0f, 0.0f }


    };
    // Vertex Array Object (VAO) 
    glGenVertexArrays(1, &gVertexAttribute);
    // bind == enable
    glBindVertexArray(gVertexAttribute);
    // this activates the first and second attributes of this VAO
    glEnableVertexAttribArray(0); 
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    // create a vertex buffer object (VBO) id
    glGenBuffers(1, &gVertexBuffer);
    // Bind the buffer ID as an ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, gVertexBuffer);
    // This "could" imply copying to the GPU, depending on what the driver wants to do...
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);

    // query where which slot corresponds to the input vertex_position in the Vertex Shader 
    GLint vertexPos = glGetAttribLocation(gShaderProgram, "vertex_position");
    // specify that: the vertex attribute "vertexPos", of 3 elements of type FLOAT, not normalized, with STRIDE != 0,
    //               starts at offset 0 of the gVertexBuffer (it is implicitly bound!)
    glVertexAttribPointer(vertexPos, 3,    GL_FLOAT, GL_FALSE,     sizeof(TriangleVertex), BUFFER_OFFSET(0));

    // query where which slot corresponds to the input vertex_color in the Vertex Shader 
    //GLint vertexColor = glGetAttribLocation(gShaderProgram, "vertex_color");
    // specify that: the vertex attribute "vertex_color", of 3 elements of type FLOAT, not normalized, with STRIDE != 0,
    //               starts at offset (12 bytes) of the gVertexBuffer 
    //glVertexAttribPointer(vertexColor, 3,    GL_FLOAT, GL_FALSE,     sizeof(TriangleVertex), BUFFER_OFFSET(sizeof(float)*3));

    GLint tex_coord = glGetAttribLocation(gShaderProgram, "coord");
    glVertexAttribPointer(tex_coord,   2,    GL_FLOAT, GL_FALSE,     sizeof(TriangleVertex), BUFFER_OFFSET(sizeof(float)*6));

    //cout << "Error: "<< glGetError() << endl;
}

void SetViewport()
{
    glViewport(0, 0, 640, 480);
}

void Render()
{
    // set the color TO BE used
    glClearColor(0, 0, 0, 1);
    // use the color to clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


    glUseProgram(gShaderProgram);
    glBindVertexArray(gVertexAttribute);

    glGenBuffers(1, &gu);
    glBindBuffer(GL_UNIFORM_BUFFER, gu);



    //glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(CPUvalues), &Gv);


    GLint _p = glGetUniformLocation(gShaderProgram, "p");
    GLint _m = glGetUniformLocation(gShaderProgram, "m");
    GLint _v = glGetUniformLocation(gShaderProgram, "v");

    //glActiveTexture(GL_TEXTURE0);
    //glBindTexture(GL_TEXTURE_2D, textures[0]);

    GLint texture0 = glGetUniformLocation(gShaderProgram, "texture0");
    glUniform1i(texture0, 0);
    glUniformMatrix4fv(_p, 1, GL_FALSE, &projection[0][0]);
    glUniformMatrix4fv(_m, 1, GL_FALSE, &world[0][0]);
    glUniformMatrix4fv(_v, 1, GL_FALSE, &view[0][0]);


    //GLint texture0 = glGetUniformLocation(gShaderProgram, "texture0");
    //glUniform1i(texture0, GL_TEXTURE0);

    // draw 3 vertices starting from index 0 in the vertex array currently bound (VAO), with current in-use shader
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);
    glCullFace(GL_BACK);
    cout << "Error: " << glGetError() << endl;
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    AllocConsole();
    freopen("CONOUT$", "w", stdout);
    DT = 0.016;
    MSG msg = { 0 };
    HWND wndHandle = InitWindow(hInstance); //1. Skapa fönster
    if (wndHandle)
    {
        HDC hDC = GetDC(wndHandle);

        HGLRC hRC = CreateOpenGLContext(wndHandle); //2. Skapa och koppla OpenGL context

        glewInit(); //3. Initiera The OpenGL Extension Wrangler Library (GLEW)

        SetViewport(); //4. Sätt viewport

        CreateShaders(); //5. Skapa vertex- och fragment-shaders

        CreateTriangleData(); //6. Definiera triangelvertiser, 7. Skapa vertex buffer object (VBO), 8.Skapa vertex array object (VAO)

        ShowWindow(wndHandle, nCmdShow);
        view = lookAt(vec3(0, 0, -2), vec3(0, 0, 0), vec3(0, 1, 0));
        mat4x4 sm;
        sm = scale(sm, vec3(1, 1, 1));
        mat4x4 tm;
        tm = translate(tm, vec3(0, 0, 0));
        mat4x4 rm;
        rm = mat4x4(1);
        projection = perspective<float>(M_PI*0.45, 640 / 480, 0.1, 20);


        while (WM_QUIT != msg.message)
        {
            auto start_time = chrono::high_resolution_clock::now();
            //Gv.v1 += 0.05 * DT;

            Gv.v1 += 1 * DT;
            world = tm * rotate(rm, Gv.v1, vec3(0, 1, 0)) * sm;


            if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
            {
                Render(); //9. Rendera
                SwapBuffers(hDC); //10. Växla front- och back-buffer
            }
            auto final_time = chrono::high_resolution_clock::now() - start_time;
            DT = chrono::duration_cast<std::chrono::milliseconds>(final_time).count() / (double)1000;
        }

        wglMakeCurrent(NULL, NULL);
        ReleaseDC(wndHandle, hDC);
        wglDeleteContext(hRC);
        DestroyWindow(wndHandle);
    }

    return (int) msg.wParam;
}

HWND InitWindow(HINSTANCE hInstance)
{
    WNDCLASSEX wcex = { 0 };
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInstance;
    wcex.lpszClassName = L"BTH_GL_DEMO";
    if( !RegisterClassEx(&wcex) )
        return false;

    RECT rc = { 0, 0, 640, 480 };
    AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );

    HWND handle = CreateWindow(
        L"BTH_GL_DEMO",
        L"BTH OpenGL Demo",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        rc.right - rc.left,
        rc.bottom - rc.top,
        nullptr,
        nullptr,
        hInstance,
        nullptr);

    return handle;
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch (message) 
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;      
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

HGLRC CreateOpenGLContext(HWND wndHandle)
{
    //get handle to a device context (DC) for the client area
    //of a specified window or for the entire screen
    HDC hDC = GetDC(wndHandle);

    //details: http://msdn.microsoft.com/en-us/library/windows/desktop/dd318286(v=vs.85).aspx
    static  PIXELFORMATDESCRIPTOR pixelFormatDesc =
    {
        sizeof(PIXELFORMATDESCRIPTOR),    // size of this pfd  
        1,                                // version number  
        PFD_DRAW_TO_WINDOW |              // support window  
        PFD_SUPPORT_OPENGL |              // support OpenGL  
        PFD_DOUBLEBUFFER,                 // double buffered        
        PFD_TYPE_RGBA,                    // RGBA type  
        32,                               // 32-bit color depth  
        0, 0, 0, 0, 0, 0,                 // color bits ignored  
        0,                                // no alpha buffer  
        0,                                // shift bit ignored  
        0,                                // no accumulation buffer  
        0, 0, 0, 0,                       // accum bits ignored  
        0,                                // 0-bits for depth buffer <-- modified by Stefan      
        0,                                // no stencil buffer  
        0,                                // no auxiliary buffer  
        PFD_MAIN_PLANE,                   // main layer  
        0,                                // reserved  
        0, 0, 0                           // layer masks ignored  
    };

    //attempt to match an appropriate pixel format supported by a
    //device context to a given pixel format specification.
    int pixelFormat = ChoosePixelFormat(hDC, &pixelFormatDesc);

    //set the pixel format of the specified device context
    //to the format specified by the iPixelFormat index.
    SetPixelFormat(hDC, pixelFormat, &pixelFormatDesc);

    //create a new OpenGL rendering context, which is suitable for drawing
    //on the device referenced by hdc. The rendering context has the same
    //pixel format as the device context.
    HGLRC hRC = wglCreateContext(hDC);

    //makes a specified OpenGL rendering context the calling thread's current
    //rendering context. All subsequent OpenGL calls made by the thread are
    //drawn on the device identified by hdc. 
    wglMakeCurrent(hDC, hRC);

    return hRC;
}

BTH_IMAGE_WIDTH, BTH_IMAGE_HEIGHT and BTH_IMAGE_DATA is defined in bth_image.h the following is what its defined as (will not include the entire definition of BTH_IMAGE_DATA due to its large size)

const unsigned int BTH_IMAGE_WIDTH = 64;
const unsigned int BTH_IMAGE_HEIGHT = 64;

//Image data stored in 8-bit RGBA format
unsigned char BTH_IMAGE_DATA[] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,16,16,16,16,16........... 
};

Calling sizeof(BTH_IMAGE_DATA) gives 16384 which matches its size as an uncompressed 8-bit 64x64 RGBA encoded texture (its hardcoded as part of the university assignment and i cant do much about that)


Solution

  • This crash has nothing to do with the texture, but with how you set up your vertex attribute array pointers.

    The GL will fetch the attribute values from each array which you enabled, and you enable the attribute arrays for the generic vertex attributes 0, 1 and 2 here:

    glBindVertexArray(gVertexAttribute);
    // this activates the first and second attributes of this VAO
    glEnableVertexAttribArray(0); 
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    

    later, you set attribute pointers for two arrays:

    GLint vertexPos = glGetAttribLocation(gShaderProgram, "vertex_position");
    glVertexAttribPointer(vertexPos, 3,    GL_FLOAT, GL_FALSE,     sizeof(TriangleVertex), BUFFER_OFFSET(0));
    
    GLint tex_coord = glGetAttribLocation(gShaderProgram, "coord");
    glVertexAttribPointer(tex_coord,   2,    GL_FLOAT, GL_FALSE,     sizeof(TriangleVertex), BUFFER_OFFSET(sizeof(float)*6));
    

    There are two bugs here:

    1. Your first part assumes that 0, 1, 2 are the relevant attribute indices used by your shader. Since you never explicitly assign the attribute locations manually (via layout(location=...) in the vertex shader code, or via glBindAttribLocation before linking the program, this assumption is just completely wrong. The GL is not required to assign attribute locations sequentially, beginging from 0 (though most do), and you also can't assume any specific order.

    2. You enabled 3 arrays, but set only an attribute pointer for two. In the best case, two of these will be actually two of the ones you enabled, in the worst case, those are completely distinct sets - be it as it may, you still have at least one enabled array for which you never set a pointer. The default vertex pointer is just NULL in client memory. As a result, you draw call will try to dereference the NULL pointer, and the OS will kill your process in the attempt, as it should.