raysan5 / raylib

A simple and easy-to-use library to enjoy videogames programming
http://www.raylib.com
zlib License
21.71k stars 2.2k forks source link

[rlgl] `glVertexAttribPointer()` error client-side with WebGL 2.0 (OpenGL ES 3.0) #4330

Open tomlikesnakes opened 4 days ago

tomlikesnakes commented 4 days ago

Hello,

There is an error when targetting webgl2 and using whatever Draw function you want (like DrawRectangle/cube/text etc)

index.js:7373 Uncaught TypeError: Cannot set properties of undefined (setting 'clientside')
    at _glVertexAttribPointer

I did a very minimal example here :

#include <raylib.h>
#include <emscripten.h>

const int screenWidth = 800;
const int screenHeight = 450;

void draw(void) {
    BeginDrawing();

    ClearBackground(RAYWHITE);

    DrawRectangle(200, 150, 400, 200, RED);

    DrawText("Hello, WebAssembly with Raylib!", 210, 200, 20, BLACK);

    EndDrawing();
}

int main() {
    InitWindow(screenWidth, screenHeight, "raylib with Emscripten - Simple Draw");

    SetTargetFPS(60);

    emscripten_set_main_loop(draw, 0, 1);

    CloseWindow();

    return 0;
}

compilation flag :

emcc -o index.html testthatfail.c -I/home/tom/raylib-wasm/src     
-L/home/tom/raylib-wasm/src -lraylib -DPLATFORM_WEB     -s USE_GLFW=3 -s FULL_ES3=1 -s MIN_WEBGL_
VERSION=2 -s MAX_WEBGL_VERSION=2 -s ASYNCIFY

I tried with only full_es2, with different things for memory and so.

For this kind of things it is not really problematic but for my other project I need to create vertex buffer and go with opengl (not rlgl) and use shaders for a cube, and i need the webgl2/opengl300es for my futures things on the project

tomlikesnakes commented 4 days ago

Okay it's due to the flag FULL_ES from emscripten

tomlikesnakes commented 4 days ago

We got these warning when removed the -s FULL_ES2=1 (or FULL_ES3=1) that transform themselves to errors

128WebGL: INVALID_VALUE: vertexAttribPointer: index out of range 128WebGL: INVALID_VALUE: enableVertexAttribArray: index out of range index.js:5086 WebGL: too many errors, no more errors will be reported to the console for this context.

tomlikesnakes commented 3 days ago

this works :

#include <raylib.h>
#include <emscripten.h>
#include <GLES3/gl3.h> 

const int screen_width = 800;
const int screen_height = 450;

GLuint shader_program;
GLint pos_attrib;
GLuint vbo, vao;

// Vertex data for a simple triangle
float vertices[] = {
    -0.5f, -0.5f, 0.0f, 
     0.5f, -0.5f, 0.0f, 
     0.0f,  0.5f, 0.0f
};

void draw(void) {
    BeginDrawing();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(shader_program);
    TraceLog(LOG_INFO, "Using shader program %d", shader_program);

    glBindVertexArray(vao);
    TraceLog(LOG_INFO, "Bound VAO %d", vao);

    glDrawArrays(GL_TRIANGLES, 0, 3);
    TraceLog(LOG_INFO, "DrawArrays executed");

    EndDrawing();
}

void setup_shaders() {
    // Vertex shader that uses the 'position' attribute
    const char* vertex_shader_source = "#version 300 es\n"
        "precision mediump float;\n"
        "layout(location = 0) in vec3 position;\n"
        "void main() {\n"
        "    // Use each component of position and assign it to gl_Position\n"
        "    gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
        "}\n";

    // Fragment shader that uses frag_color
    const char* fragment_shader_source = "#version 300 es\n"
        "precision mediump float;\n"
        "out vec4 frag_color;\n"
        "void main() {\n"
        "    // Output a constant red color\n"
        "    frag_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
        "}\n";

    GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
    glCompileShader(vertex_shader);

    GLint vertex_compile_status;
    glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &vertex_compile_status);
    if (!vertex_compile_status) {
        char info_log[512];
        glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
        TraceLog(LOG_ERROR, "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s", info_log);
    }

    GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
    glCompileShader(fragment_shader);

    GLint fragment_compile_status;
    glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &fragment_compile_status);
    if (!fragment_compile_status) {
        char info_log[512];
        glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
        TraceLog(LOG_ERROR, "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s", info_log);
    }

    // Create shader program and link shaders
    shader_program = glCreateProgram();
    glAttachShader(shader_program, vertex_shader);
    glAttachShader(shader_program, fragment_shader);
    glLinkProgram(shader_program);

    // Validate shader program
    GLint is_linked;
    glGetProgramiv(shader_program, GL_LINK_STATUS, &is_linked);
    if (!is_linked) {
        char info_log[512];
        glGetProgramInfoLog(shader_program, 512, NULL, info_log);
        TraceLog(LOG_ERROR, "ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s", info_log);
    }

    // Use the shader program
    glUseProgram(shader_program);

    // Get attribute location
    pos_attrib = glGetAttribLocation(shader_program, "position");
    TraceLog(LOG_INFO, "Attribute 'position' location (queried in setup): %d", pos_attrib);

    // Create VAO and VBO for triangle
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Enable the attribute array and set up pointer
    if (pos_attrib >= 0) {
        glEnableVertexAttribArray(pos_attrib);
        glVertexAttribPointer(pos_attrib, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
        TraceLog(LOG_INFO, "VertexAttribPointer set for location %d", pos_attrib);
    } else {
        TraceLog(LOG_ERROR, "Invalid attribute location for 'position'");
    }

    // Unbind VAO (optional)
    glBindVertexArray(0);
}

int main() {
    InitWindow(screen_width, screen_height, "raylib with Emscripten - Proper Shaders");

    // Setup the shaders and VBO/VAO
    setup_shaders();

    SetTargetFPS(60);

    // Emscripten loop
    emscripten_set_main_loop(draw, 0, 1);

    CloseWindow();

    return 0;
}

this not (cause of previous warning from enableVertexAtribArray and so) :

#include <raylib.h>
#include <emscripten.h>
#include <rlgl.h>
#include <GLES3/gl3.h>  

const int screen_width = 800;
const int screen_height = 450;

Shader custom_shader;
unsigned int vbo, vao;
int position_loc;

void draw(void) {
    BeginDrawing();

    ClearBackground(RAYWHITE);

    rlEnableShader(custom_shader.id);

    // Bind the VAO (which contains the VBO with our vertex data)
    glBindVertexArray(vao);  // Use OpenGL to bind the VAO

    // Draw the triangle using the bound VAO
    glDrawArrays(GL_TRIANGLES, 0, 3);  // Draw 3 vertices (1 triangle)

    // Unbind the VAO after drawing
    glBindVertexArray(0);  // Unbind VAO to avoid issues later

    // End custom shader mode
    rlDisableShader();

    // Render some debug text
    DrawText("Triangle rendered using OpenGL and rlgl!", 210, 400, 20, BLACK);

    EndDrawing();
}

void setup_gl_buffers() {
    // Define the vertex data for a simple triangle (position only)
    float vertices[] = {
        // Positions
        -0.5f, -0.5f, 0.0f,   // Bottom-left
         0.5f, -0.5f, 0.0f,   // Bottom-right
         0.0f,  0.5f, 0.0f    // Top-center
    };

    // Generate and bind a VAO
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    // Generate and bind a VBO
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    // Upload the vertex data to the VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Use the location retrieved from `rlgl` instead of hardcoding 0
    glEnableVertexAttribArray(position_loc);  // Use attribute location from `rlgl`
    glVertexAttribPointer(position_loc, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    // Unbind the VBO and VAO to avoid accidental modification
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

void setup_rlgl_shader() {
    // Vertex shader with a 'position' attribute
    const char* vertex_shader_code = "#version 300 es\n"
        "precision mediump float;\n"
        "layout(location = 0) in vec3 position;\n"  // 'position' attribute
        "void main() {\n"
        "    gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"  // Use each component of position
        "}\n";

    // Fragment shader outputting a red color
    const char* fragment_shader_code = "#version 300 es\n"
        "precision mediump float;\n"
        "out vec4 frag_color;\n"
        "void main() {\n"
        "    frag_color = vec4(1.0, 0.0, 0.0, 1.0);\n"  // Output red color
        "}\n";

    // Load custom shader from code
    custom_shader = LoadShaderFromMemory(vertex_shader_code, fragment_shader_code);

    // Check for any shader compilation/linking errors
    if (custom_shader.id == 0) {
        TraceLog(LOG_ERROR, "Shader program failed to load.");
        return;
    }

    // Get the location of the 'position' attribute from the custom shader
    position_loc = GetShaderLocationAttrib(custom_shader, "position");
    TraceLog(LOG_INFO, "Shader position attribute location: %d", position_loc);

    if (position_loc == -1) {
        TraceLog(LOG_ERROR, "Failed to get position attribute location");
        return;
    }
}

int main() {
    InitWindow(screen_width, screen_height, "raylib with Emscripten - OpenGL + rlgl");

    // Setup the OpenGL buffers (VBO/VAO)
    setup_gl_buffers();

    // Setup the custom shader
    setup_rlgl_shader();

    SetTargetFPS(60);

    // Emscripten loop
    emscripten_set_main_loop(draw, 0, 1);

    // Cleanup
    UnloadShader(custom_shader);  // Unload the shader when done
    glDeleteBuffers(1, &vbo);     // Delete the VBO
    glDeleteVertexArrays(1, &vao); // Delete the VAO
    CloseWindow();                // Close the window and OpenGL context

    return 0;
}

-> the way rlgl manage shaders is not consistent that's why for the second example I also tried to use opengl with rlgl (also tried only rlgl but got same warnings) -> From what I see for opengl 300es webgl2 you always need shaders and you always need to really have all in/out var clearly pass and defined (like each components of pos) and manually create vao/vbo with vertex data

for now I'm going to use pure opengl cause I really need the opengl 300es, If someone can have a look and correct what I found or anything I'm here

raysan5 commented 3 days ago

@tomlikesnakes Thanks for reporting! Actually, OpenGL ES 3.0 has been added recently as a backend GL option to raylib and it has not been widely tested.