2016-01-24 3 views
1

Вот моя маленькая функция У меня возникли проблемы с:чтение файла с станд :: ifstream скомпилировать OpenGL шейдер не работает

GLuint LoadShaders() 
{ 
    // open and compile vertex shader 
    std::ifstream VSS("glfwtest.vert", std::ifstream::ate); 
    int len = VSS.tellg() + 1; 
    GLchar* vertexSource = new GLchar[len]; 
    VSS.clear(); 
    VSS.seekg(0, VSS.beg); 
    for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); } 

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); 
    glShaderSource(vertexShader, 1, &vertexSource, NULL); 
    glCompileShader(vertexShader); 
    VSS.close(); 

    // open and compile the fragment shader 
    std::ifstream FSS("glfwtest.frag", std::ifstream::ate); 
    len = FSS.tellg() + 1; 
    GLchar* fragmentSource = new GLchar[len]; 
    FSS.clear(); 
    FSS.seekg(0, FSS.beg); 
    for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); } 

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL); 
    glCompileShader(fragmentShader); 
    FSS.close(); 

    // Link the vertex and fragment shader into a shader program 
    GLuint shaderProgram = glCreateProgram(); 
    glAttachShader(shaderProgram, vertexShader); 
    glAttachShader(shaderProgram, fragmentShader); 
    glBindFragDataLocation(shaderProgram, 0, "outColor"); 
    glLinkProgram(shaderProgram); 
    return shaderProgram; 
} 

Я следовал this руководство, и мне удалось оказать белого треугольника. После этого я подумал, что это может быть хорошим упражнением, чтобы разделить все это на функции, чтобы повторно использовать их позже. Поэтому я поместил свой шейдерный исходный код для обоих шейдеров в отдельные файлы (вместо этого они были непосредственно в моем источнике на C++) и написал вышеприведенную функцию, чтобы прочитать их из соответствующих файлов.

Теперь вот улов: мой вершинный шейдер загружается и компилируется отлично, в то время как мой шейдер фрагмента не работает. Но когда я положил фрагмент шейдер в код, как этот

const GLchar* fragmentSource = 
    "#version 150 core\n" 
    "out vec4 outColor;" 
    "void main()" 
    "{" 
    " outColor = vec4(1.0, 1.0, 1.0, 1.0);" 
    "}"; 

GLuint LoadShaders() 
{ 

    [...] 

    // open and compile the fragment shader 
    //std::ifstream FSS("glfwtest.frag", std::ifstream::ate); 
    //len = FSS.tellg() + 1; 
    //GLchar* fragmentSource = new GLchar[len]; 
    //FSS.clear(); 
    //FSS.seekg(0, FSS.beg); 
    //for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); } 

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL); 
    glCompileShader(fragmentShader); 

    [...] 

    return shaderProgram; 
} 

он отлично работает и показывает белый треугольник. Таким образом, тот же код, который загружает и компилирует мой вершинный шейдер из «glfwtest.vert», не работает для шейдера фрагментов.

Я никогда не работал с <fstream> в C++ и собирал его из online documentation example. Я предполагаю, что ошибка там где-то есть, но я не могу ее найти, так как я не мастер C++ (пока).

EDIT:

После предложения в комментариях, я до сих пор застрял. Вот вывод GDB я получаю:

(gdb) break main.cpp:55 
Breakpoint 1 at 0x4017dc: file main.cpp, line 55. 
(gdb) r 
Starting program: /home/georg/code/redbook/glfwtest/glfwtest 
[Thread debugging using libthread_db enabled] 
Using host libthread_db library "/usr/lib/libthread_db.so.1". 

Breakpoint 1, LoadShaders() at main.cpp:56 
56  GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 
(gdb) p vertexSource 
$1 = (GLchar *) 0x8277e0 "#version 420 core\n\nin vec2 position;\n\nvoid main()\n{\n gl_Position = vec4(position, 0.0, 1.0);\n}" 
(gdb) p fragmentSource 
$2 = (GLchar *) 0x82a300 "#version 150 core\nout vec4 outColor;\nvoid main()\n{\n outColor = vec4(1.0, 1.0, 1.0, 1.0);\n}\377\177" 
(gdb) p len 
$3 = 92 
(gdb) 

, как вы можете видеть, что длина правильно хранится в len, есть 92 символов в источнике. Тем не менее, как-то есть два дополнительных символа. Я проверил исходный файл даже с шестнадцатеричным редактором, эти два символа определенно не находятся в файле.

+1

Возможно, вам не хватает '\ 0' в конце строк чтения? – BDL

+1

'GLchar * vertexSource = new GLchar [len];' Вы забыли удалить их? Потому что это очень важно. Это не ваша проблема, но это все еще важно. –

+0

@BDL: Я пропустил это, но кажется, что «glCompileShader()» не возражает, если отсутствует ограничитель строки, поскольку вершинный шейдер компилируется отлично. Я добавил его в любом случае, поэтому спасибо за этот намек. – georgjz

ответ

0

Хотя этот ответ не отлаживает ваш код, вы выразили заинтересованность в том, чтобы ваши подпрограммы были более модульными. Вот кое-что немного более крепок, что делает использование std::stringstream и устраняет явное динамическое управление памятью:

static std::string read_shader_file (const char *shader_file) 
{ 
    // no feedback is provided for stream errors/exceptions. 

    std::ifstream file (shader_file); 
    if (!file) return std::string(); 

    file.ignore(std::numeric_limits<std::streamsize>::max()); 
    auto size = file.gcount(); 

    if (size > 0x10000) // 64KiB sanity check for shaders: 
     return std::string(); 

    file.clear(); 
    file.seekg(0, std::ios_base::beg); 

    std::stringstream sstr; 
    sstr << file.rdbuf(); 
    file.close(); 

    return sstr.str(); 
} 

std::string перемещаемый, так что вам не нужно беспокоиться о стоимости возвращения на «ценности» - хотя это было бы тривиально по сравнению с компиляцией шейдеров GL и привязкой в ​​любом случае.

GLuint LoadShaders() 
{ 
    ... 

    std::string shader_source = read_shader_file("glfwtest.vert"); 

    const char *shader_source_ptr = shader_source.c_str(); 
    glShaderSource(vertexShader, 1, & shader_source_ptr, NULL); 
    ... 
} 
+0

Спасибо, Бретт Хейл! Это сработало просто отлично, мне пришлось немного подкрутить его, добавив 'const char * fragmentSource = fragment_shader_source.c_str(); glShaderSource (фрагментShader, 1, & fragmentSource, NULL); ', иначе он не будет компилироваться. Кроме того, благодаря всем остальным комментаторам, я чувствую, что мой код сегодня значительно улучшился :) – georgjz

+0

@georgjz - извините. Я пропустил шаг, изменяя исходный код. –

+0

проблем нет. Забавно, я попробовал аналогичное решение, прежде чем пытаться использовать выше. Я уже совсем близко вижу. – georgjz

0

Проблема была диагностирована @derhass в комментариях, но так как OP еще есть проблема на самом деле фиксируя код:

Вы должны убедиться, что строка заканчивается нулем перед вами перейдите к glShaderSource(). Если вы не сделаете этого, то будет неопределенный характер в конце строки, и почти все, что может случиться:

  • Она могла бы работать, если персонаж случается '\0'.
  • Это может привести к синтаксической ошибке, основанной на недопустимом символе.
  • Это может привести к сбою, если нулевой символ не найден до того, как он убежит с конца доступной памяти.

Чтобы исправить это с минимальными изменениями в коде, используйте:

std::ifstream VSS("glfwtest.vert", std::ifstream::ate); 
int len = VSS.tellg(); 
GLchar* vertexSource = new GLchar[len + 1]; 
VSS.clear(); 
VSS.seekg(0, VSS.beg); 
for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); } 
vertexSource[len] = '\0'; 

То же самое для пиксельный шейдер.

+0

Я тоже попробовал ваше решение, и он отлично работает. Спасибо! – georgjz

Смежные вопросы