【问题标题】:OpenGL Shader Compilation Issue -- Unexpected EOFOpenGL 着色器编译问题——意外的 EOF
【发布时间】:2011-06-19 06:32:55
【问题描述】:

所以我决定尝试使用 Java 编写一个简单的 OpenGL 应用程序,只是为了看看它与我的其他工作相比如何,我遇到了我的着色器拒绝编译的问题。他们真的再简单不过了,这是我的顶点着色器来证明我的意思:

//Minimal vertex shader

#version 330

in vec3 in_Position;
in vec3 in_Color;
out vec3 var_Color;

void main(void)
{
    gl_Position = vec4(in_Position, 1.0);
    var_Color = in_Color;
}

片段着色器同样简单,所以除非有人要求,否则我不会费心发布它。当我检查日志时,我得到了以下错误(对于两个着色器):

(0) : error C0000: syntax error, unexpected $end at token "<EOF>"

我不确定这是否相关...但我正在使用 Java 在 Linux (Ubuntu 11.04) 上进行开发。我正在使用的唯一库是 JOGL(用于 openGL 绑定)和标准 Java 库(如果这算数的话……)我的显卡是 Nvidia GeForce 9600M GS,我检查了扩展,它完全支持OpenGL 3.3。

帮助我 Stack Overflow,你是我唯一的希望。

编辑:

根据要求,这里是负责加载和编译着色器源的函数。另外,说到 GLSL,我是一个超级 n00b,所以我真的不知道要寻找什么来确保为 OpenGL 正确格式化。一个指向最近(即处理 OpenGL 3.x)关于该主题的教程的链接将不胜感激。

private int CreateCompiledShader(File source, int shader, GL3 gl){
        int shaderloc = gl.glCreateShader(shader);

        BufferedReader input = null;
        ArrayList<String> lines = new ArrayList<String>();
        ArrayList<Integer> lengths = new ArrayList<Integer>();
        try{
            input = new BufferedReader(new FileReader(source));
            String buffer;

            while(input.ready()){
                buffer = input.readLine();
                lines.add(buffer);
                lengths.add(buffer.length());
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(input != null){
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        int[] iLengths = new int[lengths.size()];
        for(int i = 0; i < lengths.size(); i++){
        iLengths[i] = lengths.get(i).intValue();
        }

        gl.glShaderSource(shaderloc, lines.size(), lines.toArray(new String[0]), iLengths, 0);
        gl.glCompileShader(shaderloc);

        int error = gl.glGetError();
        if(error != GL3.GL_NO_ERROR){
            Logger.log(new GLU().gluErrorString(error), Logger.ERROR, "Shader compilation");
        }

        return shaderloc;
    }

顺便说一句,我检查 glGetError() 的 if 语句实际上不是错误被捕获的地方,直到执行返回调用函数并且我检查着色器日志。可能是相关的,但话又说回来,我也可能是漫无边际的。

【问题讨论】:

  • 尝试在最后一个括号后添加空行,这有时可能会有所帮助。
  • 这是我尝试的第一件事,但是当我发布代码时它没有显示出来。

标签: opengl shader jogl


【解决方案1】:

好的,现在我看到了问题所在。您的加载代码不起作用。但别担心;很多人看到 glShaderSource 接受一个字符串数组时会感到困惑。我猜你看到有人这样用 C/C++ 编写着色器:

const char *myShader[] = {
    "#version 330\n",
    "\n",
    "in vec3 in_position;\n",
    ...
};

他们上传了着色器,glShaderSource(shader, ARRAY_COUNT(myShader), myShader, NULL, 0);

虽然这是合法的,但这并不是该功能的真正用途。因为 GLSL 没有#include 机制,所以 glShaderSource 可以接受多个字符串。每个字符串都是着色器文件。然后,着色器编译器会像编译前的 #include 一样有效地将字符串连接在一起。

现在,正因为如此,您可以将每一行作为单独的字符串。但是,回头看看那个 C/C++ 代码。看看每一行的末尾是什么?那个 '\n' 字符。

这就是您正在加载的行末尾的不是。因为我很确定 BufferedReader.readline 确实 not 保留了行尾字符。所以你的着色器看起来像:

//Minimal vertex shader#version 330in vec3 in_Position;in vec3 in_Color;out vec3 var_Color;...

您的整个着色器被视为一个大的单行注释。因此意外的 EOF:OpenGL 从来没有看到任何要编译的东西;)

您不应该逐行读取文件。只需将整个内容加载到一个字符串中即可。然后将其传递给 OpenGL。或者,您可以查看this previous answer 关于 JOGL;它应该向您展示如何正确地执行此操作(尽管我希望 BufferedReader 能够以某种方式将整个文件作为字符串而不是逐行读取。

【讨论】:

  • 啊哈!这就说得通了。我以前在 C/C++ 中完成过这个项目,所以当我在 javadoc 中看到这个函数采用 String[] 时,不知何故我认为 char* == String[] (这显然是不正确的)。感谢您提供有用的答案。
【解决方案2】:

虽然已经提供并接受了一个答案,但我将在这里写下我更喜欢它的方式:

// Create shader from one or multiple source files
private int CreateCompiledShader(File[] source_files, int shader, GL3 gl){
    int shaderloc = gl.glCreateShader(shader);
    int nSources = source_files.size();

    // the number of shader sources it known from the beginning
    // so we can allocate the arrays right here
    String[] sources = new String[nSources];
    int[] sources_lengths = new int[nSources];
    for(int i = 0; i < nSources; i++) {
        // We don't need a buffered reader as we're reading the file as a whole
        FileReader input = new FileReader(source_file[i]);
        String buffer;

        buffer = input.read();
        sources[i] = buffer;
        sources_lengths[i] = buffer.length();

        if(input != null){
            input.close();
        }
    }

    // Frankly I really don't understand why you have to pass sources_lengths here
    // It would take only 3 LOC in the binding's code
    // to extract that from the sources array, Oh, well...
    gl.glShaderSource(shaderloc, nSources, sources, sources_lengths, 0);
    gl.glCompileShader(shaderloc);

    // Actually if glGetError() returns an error you must call it in a loop
    // as OpenGL errors can accumulate, and you have to pop them all from the list.
    int error = gl.glGetError();
    if(error != GL3.GL_NO_ERROR){
        Logger.log(new GLU().gluErrorString(error), Logger.ERROR, "Shader compilation");
    }

    return shaderloc;
}

我冒昧地删除了所有 try/catch/finally 块,因为它们有点放错了位置:如果读取任何源文件失败,着色器加载将无法完成,因此优雅地继续是没有意义的。处理这个问题的正确方法是围绕这个大的 try/catch 块,它清理着色器编译未完成的 OpenGL 对象。

【讨论】:

  • 如果您不是使用 Java 编程,而是使用具有指针的语言(C、C++、D、Go),那么另一种可行的方法是尝试对文件进行内存映射,而不是将其读取到缓冲区。
【解决方案3】:

尝试将注释移到#version 声明之后。没关系,但可能存在驱动程序错误。

另外,请尝试在文件末尾添加一个额外的空行。再次,只是为了确定。

最后,确保您实际上正确加载了字符串。在调试器中检查它。并确保您正确地将字符串传递给 OpenGL。这段代码是什么样的?

【讨论】:

  • 您可能还想确保文件开头没有 BOM。 (这是大多数编辑器会对您隐藏的 3 字节内容,它定义了文件中的 unicode 编码,即使它只使用拉丁字符)
猜你喜欢
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
相关资源
最近更新 更多