проблема компиляции шейдера glsl во время выполнения - PullRequest
5 голосов
/ 06 ноября 2011

Я работаю над проектом, в котором используются шейдеры OpenGL 4.0.

Я должен предоставить вызов glShaderSource () с массивом массивов символов, который представляет источникшейдер.

Компиляция шейдера не удалась со следующими ошибками:

(0) : error C0206: invalid token "<null atom>" in version line
(0) : error C0000: syntax error, unexpected $end at token "<EOF>"

Вот мой (привет мир) шейдер - прямо из кулинарной книги OpenGL 4.0 на языке шейдеров

#version 400

in  vec3        VertexPosition;
in  vec3        VertexColor;
out vec3        Color;

void main()
{
    Color       = VertexColor;
    gl_Position = vec4( VertexColor, 1.0 );
}

А вот мой код для чтения файла шейдера в мой код C ++ и компиляции шейдера во время выполнения:

const int       nMaxLineSize    = 1024;
char            sLineBuffer[nMaxLineSize];
ifstream        stream;
vector<string>  vsLines;
GLchar**        ppSrc;
GLint*          pnSrcLineLen;
int             nNumLines;


stream.open( m_sShaderFile.c_str(), std::ios::in );

while( (stream.good()) && (stream.getline(sLineBuffer, nMaxLineSize)) )
{
    if( strlen(sLineBuffer) > 0 )
        vsLines.push_back( string(sLineBuffer) );
}

stream.close();


nNumLines       = vsLines.size();
pnSrcLineLen    = new GLint[nNumLines];
ppSrc           = new GLchar*[nNumLines];

for( int n = 0; n < nNumLines; n ++ )
{
    string &    sLine       = vsLines.at(n);
    int         nLineLen    = sLine.length();
    char *      pNext       = new char[nLineLen+1];

    memcpy( (void*)pNext, sLine.c_str(), nLineLen );                
    pNext[nLineLen] = '\0'; 

    ppSrc[n]        = pNext;
    pnSrcLineLen[n] = nLineLen+1;
}
vsLines.clear();

// just for debugging purposes (lines print out just fine..)
for( int n = 0; n < nNumLines; n ++ )           
    ATLTRACE( "line %d: %s\r\n", n, ppSrc[n] );

// Create the shader
m_nShaderId = glCreateShader( m_nShaderType );

// Compile the shader
glShaderSource( m_nShaderId, nNumLines, (const GLchar**)ppSrc, (GLint*) pnSrcLineLen );
glCompileShader( m_nShaderId );

// Determine compile status
GLint nResult = GL_FALSE;
glGetShaderiv( m_nShaderId, GL_COMPILE_STATUS, &nResult );

Код C ++ выполняется должным образом, но компиляция шейдера не удалась.Кто-нибудь может определить, что я могу делать неправильно?

У меня такое ощущение, что это может быть как-то связано с символами конца строки, но так как это моя первая попытка компиляции шейдеров, я застрял!

Я читал другиеТАК ответы на компиляцию шейдеров, но они кажутся специфичными для Java / других языков, а не C ++.Если это поможет, я на платформе win32.

Ответы [ 3 ]

15 голосов
/ 06 ноября 2011

Вы допустили ошибку, которую сделали другие.Это определение glShaderSource:

void glShaderSource(GLuint shader,  GLsizei count,  const GLchar **string,  const GLint *length);

string - это массив строк.Это , а не , предназначенный для массива строк в вашем шейдере .Компилятор будет интерпретировать этот массив строк, объединяя их друг с другом, один за другим.Без перевода строки.

Поскольку stream.getline будет , а не вставит символ \n в строку, каждая из генерируемых вами строк шейдера не будет иметь символа новой строки в конце.Поэтому, когда glShaderSource идет на их компиляцию, ваш шейдер будет выглядеть так:

#version 400in  vec3        VertexPosition;in  vec3        VertexColor;out vec3        Color;...

Это недопустимый GLSL.

Правильный способ сделать это - загрузить файл как

std::ifstream shaderFile(m_sShaderFile.c_str());
if(!shaderFile)
  //Error out here.
std::stringstream shaderData;
shaderData << shaderFile.rdbuf();  //Loads the entire string into a string stream.
shaderFile.close();
const std::string &shaderString = shaderData.str(); //Get the string stream as a std::string.

Тогда вы можете просто передать это glShaderSource достаточно просто:

m_nShaderId = glCreateShader( m_nShaderType );
const char *strShaderVar = shaderString.c_str();
GLint iShaderLen = shaderString.size();
glShaderSource( m_nShaderId, 1, (const GLchar**)&strShaderVar, (GLint*)&iShaderLen );
glCompileShader( m_nShaderId );

Если вы скопировали этот код загрузки откуда-то, я настоятельно рекомендую вам найти другое место, чтобы узнать об OpenGL .Потому что это ужасное кодирование.

3 голосов
/ 07 ноября 2011

glShaderSource (m_nShaderId, nNumLines, (const GLchar **) ppSrc, (GLint *) pnSrcLineLen);

Я знаю, что подпись glShaderSource выглядит соблазнительно для отправки каждой строки шейдера отдельно. Но это то, для чего он предназначен. Возможность отправки нескольких массивов заключается в том, что можно смешивать несколько источников примитивных шейдеров в один шейдер, например, включать файлы. Понимание этого значительно упрощает чтение в файле шейдера и позволяет избежать таких неприятных ошибок.

Используя C ++, вы можете сделать это намного лучше и чище. Я уже написал следующее в Получение символов мусора при чтении файлов GLSL


Вы используете C ++, поэтому я предлагаю вам использовать это. Вместо чтения в самораспределенный массив символов, я предлагаю вам прочитать в std :: string:

#include <string>
#include <fstream>

std::string loadFileToString(char const * const fname)
{
    std::ifstream ifile(fname);
    std::string filetext;

    while( ifile.good() ) {
        std::string line;
        std::getline(ifile, line);
        filetext.append(line + "\n");
    }

    return filetext;
}

Это автоматически берет на себя все распределение памяти и правильное разграничение - ключевое слово RAII: распределение ресурсов - инициализация. Позже вы можете загрузить исходный код шейдера с помощью

void glcppShaderSource(GLuint shader, std::string const &shader_string)
{
    GLchar const *shader_source = shader_string.c_str();
    GLint const shader_length = shader_string.size();

    glShaderSource(shader, 1, &shader_source, &shader_length);
}

Вы можете использовать эти две функции вместе, как это:

void load_shader(GLuint shaderobject, char * const shadersourcefilename)
{
    glcppShaderSource(shaderobject, loadFileToString(shadersourcefilename));
}
1 голос
/ 06 ноября 2011

Просто быстрое предположение:

Вы пытались вызвать glShaderSource с NULL в качестве параметра длины?В этом случае OpenGL будет считать ваш код недействительным.

(отредактировано из-за глупости)

...