我开始创建一个Qt OpenGL应用程序。我遵循了Learnopengl中的教程。在本教程中,它给出了与顶点和片段着色器对应的静态C-String。当我向我的应用程序提供一个静态C-String着色器时,没有错误,我的三角形出现在屏幕上。
但是当我尝试处理所有着色器代码以分离文件时,我在glLinkProgram命令期间出错,例如 必须写入gl_Position
这是我的C ++代码:
#include "widget.h"
Widget::Widget(QOpenGLWidget *parent)
: QOpenGLWidget(parent)
{
this->setWindowTitle("Learning OpenGL");
QString pathV="shaders/main.vert";
QString pathF="shaders/main.frag";
vertexShaderSource=loadShader(pathV);
fragmentShaderSource=loadShader(pathF);
}
Widget::~Widget()
{
}
void Widget::initializeGL()
{
this->initializeOpenGLFunctions();
qDebug()<<"Vous travaillez sur une OpenGL version:"<<QString((char*)glGetString(GL_VERSION));
compileShaders();
glClearColor(0.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
void Widget::paintGL()
{
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 1.0f, 0.0f
};
GLuint VBO,VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbind
glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)
// Draw our first triangle
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
QString Widget::loadShader(QString &path)
{
QFile file(path);
file.open(QIODevice::ReadOnly);
QTextStream stream(&file);
QString lines;
while(!stream.atEnd())
{
lines.append(stream.readLine()+"\n");
}
//lines.append('\0');
file.close();
return lines;
}
void Widget::compileShaders()
{
/* //Static shaders
// Shaders
vs = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"}\0";
fs = "#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n"
"}\n\0";
//*/
//Vertex shader
GLuint vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
vs=vertexShaderSource.toStdString().c_str();
glShaderSource(vertexShader, 1,(const GLchar **) &vs, 0);
glCompileShader(vertexShader);
//check if any error during compilation
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
qDebug()<< "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog ;
}
else qDebug()<<"compile vertex ok";
//Fragment shader
GLuint fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
fs=fragmentShaderSource.toStdString().c_str();
glShaderSource(fragmentShader, 1,(const GLchar **) &fs, 0);
glCompileShader(fragmentShader);
//Check if any error during compilation
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
qDebug()<< "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog ;
}
else qDebug()<<"compile fragment ok";
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if(!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
qDebug()<< "Linkage error\n" << infoLog;
}
}
当我在控制台上显示从着色器文件中读取的数据时,它与静态C-String着色器代码相同。
所以我不明白为什么我的着色器文件会崩溃。
答案 0 :(得分:0)
您的着色器源似乎为空或无效。最可能的原因是你正在为vs / fs变量分配临时副本缓冲区:
vs=vertexShaderSource.toStdString().c_str();
fs=fragmentShaderSource.toStdString().c_str();
这些指针可能在这些行之后无效,因此将它们传递给glShaderSource函数可能不是最好的主意。
您必须复制缓冲区内容。尝试这样的事情:
char* vs = new char[vertexShaderSource.size() + 1];
strcpy(vs, vertexShaderSource.toStdString().c_str());
答案 1 :(得分:0)
此代码:
fragmentShaderSource.toStdString()
生成字符串的临时副本。该临时表将在生成它的表达式后被销毁。因此,当您在该字符串上调用c_str()
时,您将获得一个指向即将删除的内存的指针。
相反,您的fragmentShaderSource
和类似的顶点着色器类型应该只是std::string
本身。因此,loadShader
应返回std::string
(返回lines.toStdString()
而非lines
)。
完成后,您可以fragmentShaderSource.c_str()
并将其自由传递给OpenGL。