使用GLAD和GLFW 3的OpenGL应用程序(openGL 4.1/pureC/MacOS)出现分段故障

9bfwbjaz  于 2023-02-15  发布在  Mac
关注(0)|答案(1)|浏览(199)

我正在努力学习OpenGL和C,方法是阅读www.example.com教程和JDAH系列关于OpenGL in C的视频。我尤其受到了他的这个项目的启发:learnopengl.com tutorial and the JDAH series of videos about OpenGL in C. I especially got really inspired by this project of him : minecraft in one week .
在www.example.com的照明章节之前,我有一个完美工作的代码,但是我的代码很混乱,我想重构和清理,所以我把Makefile改为JDAH的,把代码分成几个文件。learnopengl.com but my code was messy and I wanted to refactor and clean up so I changed the Makefile to JDAH's one, split the code in several files. Nothing is working anymore.
我花了7个小时来调试这个问题,我发现用C调试segfault并不有趣。不管怎么说,我想我已经找到了问题所在,我第二次调用glEnableVertexAttribArray解引用了一个空指针,这导致了segfault。我试着把Glew改为Glad。我重新安装了库,试着修改Makefile,但没有任何效果,我失去了希望。
下面你可以找到我的Makefile,我的main. c的一部分和一张在lldb中显示的segfault的图片。我运行在MacOS 13.2上,使用OpenGL 4.1。我在我的/usr/local/include中安装了Glew,但它不是Makefile引用的那些,我不知道这是否会导致问题。
JDAH的代码在我的Mac上运行得很完美,几乎使用了相同的Makefile(不包括噪音),但他使用OpenGL 3.3,我使用OpenGL 4.1,所以我改变了高兴文件夹。

    • 生成文件**
UNAME_S = $(shell uname -s)

CC = clang
CFLAGS = -std=c11 -O3 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing
CFLAGS += -Wno-pointer-arith -Wno-newline-eof -Wno-unused-parameter -Wno-gnu-statement-expression
CFLAGS += -Wno-gnu-compound-literal-initializer -Wno-gnu-zero-variadic-macro-arguments
CFLAGS += -Ilib/cglm/include -Ilib/glad/include -Ilib/glfw/include -Ilib/stb -fbracket-depth=1024
LDFLAGS = lib/glad/src/glad.o lib/cglm/libcglm.a lib/glfw/src/libglfw3.a -lm

# GLFW required frameworks on OSX
ifeq ($(UNAME_S), Darwin)
    LDFLAGS += -framework OpenGL -framework IOKit -framework CoreVideo -framework Cocoa
endif

ifeq ($(UNAME_S), Linux)
    LDFLAGS += -ldl -lpthread
endif

SRC  = $(wildcard src/**/*.c) $(wildcard src/*.c) $(wildcard src/**/**/*.c) $(wildcard src/**/**/**/*.c)
OBJ  = $(SRC:.c=.o)
BIN = bin

.PHONY: all clean

all: dirs libs game

libs:
    cd lib/cglm && cmake . -DCGLM_STATIC=ON && make
    cd lib/glad && $(CC) -o src/glad.o -Iinclude -c src/glad.c
    cd lib/glfw && cmake . && make

dirs:
    mkdir -p ./$(BIN)

run: all
    $(BIN)/game

game: $(OBJ)
    $(CC) -o $(BIN)/game $^ $(LDFLAGS)

%.o: %.c
    $(CC) -o $@ -c $< $(CFLAGS)

clean:
    rm -rf $(BIN) $(OBJ)
    • main. c**(还有更多代码,但为了可读性起见,我没有包括所有代码。LLDB在第二个glEnableVertexArray上崩溃)
#include "glad/glad.h"
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cglm/cglm.h>

#include "gfx/shaders.h"

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

int main() {

    if (!glfwInit()) {
        printf("Error initializing GLFW");
        exit(EXIT_FAILURE);
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 

    glfwSetErrorCallback(error_callback);

    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);

    if (window == NULL) {
        printf("Failed to create Window\n");
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glfwSetCursorPosCallback(window, mouse_callback); 
    
    int version = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
    if (version == 0) {
        printf("Failed to initialize OpenGL context\n");
        return -1;
    }

    // Successfully loaded OpenGL
    printf("Loaded OpenGL %d.%d\n", GLVersion.major, GLVersion.minor);
    printf("test");
    printf("%s", glGetString(GL_VERSION));
    printf("test");

    //Enable Depth Buffer
    glEnable(GL_DEPTH_TEST);

    //Define viewport for the size of the window
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


    //Generate a VAO so we don't have to do this configuration for every vertices
    unsigned int VAO, VBO;
    glGenVertexArrays(1, &VAO);  
    //Get Vertex Buffer Object from OpenGL
    glGenBuffers(1, &VBO);  
    //Push vertex data into the buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(VAO);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);
    • LLDB的输出**

因为我是C的新手,我可能忘记了一些东西,请随时询问更多的信息,我会尽我所能提供给他们。

(lldb) n
Process 28686 stopped
* thread #1, stop reason = step over
    frame #0: 0x000000010000a990 game`main at main.c:238:5 [opt]
   235      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (void*)0);
   236      glEnableVertexAttribArray(0);
   237      glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (void*)(3*sizeof(float)));
-> 238      glEnableVertexAttribArray(1);
   239 
   240 
   241      unsigned int vertexShader;
Target 0: (game) stopped.
(lldb) n
Process 28686 stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00007ffa20fc4f00
->  0x7ffa20fc4f00: movl   (%rax), %edi
    0x7ffa20fc4f02: popq   %rbp
    0x7ffa20fc4f03: jmpq   *%r8
    0x7ffa20fc4f06: pushq  %rbp
Target 0: (game) stopped.
    • EDIT 1**我尝试跟踪Haris输入并捕获错误代码。结果很奇怪,我并不真正理解所有内容。我正在记录GLAD加载后的OpenGL版本,这是涉及的代码行:
// Successfully loaded OpenGL
    printf("Loaded OpenGL %d.%d\n", GLVersion.major, GLVersion.minor);
    GLenum err;
    err = glGetError();
    printf("Error: %u", err);
    printf("%s", glGetString(GL_VERSION));
    err = glGetError();
    printf("Error2: %u", err);

由于LLDB没有抱怨地遍历了这些行,我认为它们没有问题,但事实上,只是控制台中的第一个printf有问题。这个观察结果对之后的每个printf都是正确的。我尝试在每个函数调用后执行glGetError(),但控制台中什么也没有出现。

    • 控制台输出**
Loaded OpenGL 4.1
[1]    31313 segmentation fault  bin/game
    • 编辑2**

我尝试更改错误处理,使其看起来像Haris指出的那样。

// Successfully loaded OpenGL
    printf("Loaded OpenGL %d.%d\n", GLVersion.major, GLVersion.minor);
    if(!glGetString(GL_VERSION)) {
        err = glGetError();
        printf("Error %u", err);
        return -1;
    }
    printf("TEST");

    err = glGetError();
    printf("Error2: %u", err);

至于Derhass请求,你可以在下面找到我的main.c的其余部分。昨天在我开始修改makefile和libs之前,它工作得非常好。

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GL_FLOAT), (void*)(3*sizeof(float)));
    err = glGetError();
    printf("%u", err);
    glEnableVertexAttribArray(1);

    err = glGetError();
    printf("%u", err);

    unsigned int vertexShader;
    unsigned int fragmentShaderOrange = glCreateShader(GL_FRAGMENT_SHADER); // the first fragment shader that outputs the color orange
    unsigned int shaderProgramOrange = glCreateProgram();

    //const char* vertexShaderSourceFromFile = getShader(BASE_VERTEX_SHADER);

    compile_shader(&vertexShader, GL_VERTEX_SHADER, BASE_VERTEX_SHADER);
    compile_shader(&fragmentShaderOrange, GL_FRAGMENT_SHADER, BASE_FRAGMENT_SHADER);

    // link the first program object
    glAttachShader(shaderProgramOrange, vertexShader);
    glAttachShader(shaderProgramOrange, fragmentShaderOrange);
    glLinkProgram(shaderProgramOrange);
    // then link the second program object using a different fragment shader (but same vertex shader)
    // this is perfectly allowed since the inputs and outputs of both the vertex and fragment shaders are equally matched.

    unsigned int texture1;
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // set the texture wrapping/filtering options (on the currently bound texture object)
    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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load and generate the texture
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(1);  
    unsigned char *data = stbi_load("./res/tibo.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        printf("Failed to load texture.");
    }
    stbi_image_free(data);

    unsigned int texture2;
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // set the texture wrapping/filtering options (on the currently bound texture object)
    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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    data = stbi_load("./res/awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    stbi_image_free(data);

    glUseProgram(shaderProgramOrange);
    glUniform1i(glGetUniformLocation(shaderProgramOrange, "texture1"), 0);
    glUniform1i(glGetUniformLocation(shaderProgramOrange, "texture2"), 1);

    //#FREE RAM
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShaderOrange);

    mat4 projection;
    glm_perspective(glm_rad(45.0f), 800.0f / 600.0f, 0.1f, 100.0f, projection);

    unsigned  int projectionLoc = glGetUniformLocation(shaderProgramOrange, "projection");
    glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, projection[0]);

    // uncomment this call to draw in wireframe polygons.
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    while(!glfwWindowShouldClose(window)) {
        processInput(window);

        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame; 

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(shaderProgramOrange);

        glBindVertexArray(VAO);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);

        vec3 cameraTarget;
        glm_vec3_add(cameraFront, cameraPos, cameraTarget);

        mat4 view;
        glm_lookat(
            cameraPos,
            cameraTarget,
            cameraUp,
            view);

        unsigned  int viewLoc = glGetUniformLocation(shaderProgramOrange, "view");
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, view[0]);

        for(unsigned int i = 0; i < 10; i++) {
            mat4 model;
            glm_mat4_identity(model);
            glm_translate(model, cubePositions[i]);
            glm_rotate(model, glm_rad(20.0f * i), (vec3){1.0f, 0.3f, 0.5f});

            unsigned int modelLoc = glGetUniformLocation(shaderProgramOrange, "model");
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, model[0]);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

        
        //glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();    
    }

    glfwTerminate();
    return 0;
}
    • 编辑3**

我尝试了一些更老的调试方法,也就是注解每一行代码,然后一个接一个地分解它们,直到我看到崩溃。我得到了一些新的东西,一个38900 bus error bin/game,当调用我写的函数来编译着色器时。这些函数从文件中读取着色器,将它们作为字符串传递给GLSL,GLSL编译它们并返回它们。
你可以看到下面的代码片段,它是这样做的。它是工作之前,我的重构和我一定要更新我的路径时,我移动的文件

void compile_shader(GLuint* shaderId, GLenum shaderType, int shaderFilePath)
{
    GLint isCompiled = 0;
    /* Calls the Function that loads the Shader source code from a file */
    const char* shaderSource = getShader(shaderFilePath); 

    *shaderId = glCreateShader(shaderType);
    if(*shaderId == 0) {
        printf("COULD NOT LOAD SHADER: %d!\n", shaderFilePath);
    }

    glShaderSource(*shaderId, 1, (const char**)&shaderSource, NULL);
    glCompileShader(*shaderId);
    glGetShaderiv(*shaderId, GL_COMPILE_STATUS, &isCompiled);

    if(isCompiled == GL_FALSE) { /* Here You should provide more error details to the User*/
        printf("Shader Compiler Error: %d\n", shaderFilePath);
        glDeleteShader(*shaderId);
        return;
    }
}
njthzxwz

njthzxwz1#

问题解决了。我绝对是一个C新手,问题只不过是相对于源代码的相对路径,而不是相对于我运行代码的地方。我仍然很惊讶,像这样一个简单的错误,让我想考虑我的生活选择了,但我很高兴现在这个错误得到了解决。
我非常尊重我的纯C开发伙伴,用支持良好调试的语言编写代码显然是在轻松模式下编写代码。

相关问题