我有一个简单的顶点着色器
static const char *vertexShader=
"attribute vec4 vPosition; \n"
"void main(){\n"
"gl_Position = vPosition;\n"
"}";
此外,我还有一个着色器可以创建一个" Billboard"对图像的影响。
static const char *fragmentShader=
"uniform float grid;\n"
"uniform float dividerValue;\n"
"uniform float step_x;\n"
"uniform float step_y;\n"
"uniform sampler2D source;\n"
"uniform lowp float qt_Opacity;\n"
"uniform vec2 qt_TexCoord0;\n"
"void main(){\n"
"vec2 uv = qt_TexCoord0.xy;\n"
"float offx = floor(uv.x / (grid * step_x));\n"
"float offy = floor(uv.y / (grid * step_y));\n"
"vec3 res = texture2D(source, vec2(offx * grid * step_x , offy * grid * step_y)).rgb;\n"
"vec2 prc = fract(uv / vec2(grid * step_x, grid * step_y));\n"
"vec2 pw = pow(abs(prc - 0.5), vec2(2.0));\n"
"float rs = pow(0.45, 2.0);\n"
"float gr = smoothstep(rs - 0.1, rs + 0.1, pw.x + pw.y);\n"
"float y = (res.r + res.g + res.b) / 3.0;\n"
"vec3 ra = res / y;\n"
"float ls = 0.3;\n"
"float lb = ceil(y / ls);\n"
"float lf = ls * lb + 0.3;\n"
"res = lf * res;\n"
"vec3 col = mix(res, vec3(0.1, 0.1, 0.1), gr);\n"
"if (uv.x < dividerValue)\n"
"gl_FragColor = qt_Opacity * vec4(col, 1.0);\n"
"else\n"
"gl_FragColor = qt_Opacity * texture2D(source, uv);\n"
"}";
我想要做的是使用此着色器将此效果应用于QtOpenGlWidget中的图像。但我不知道如何将我的图像设置为纹理并将其传递给着色器,然后返回使用着色器效果修改的图像。我想要实现的是:https://imgur.com/a/NSY0u但我的着色器不会影响图像https://imgur.com/a/dgSfq。我的GLWidget类:
GLWidget::GLWidget(Helper *helper, QWidget *parent)
: QOpenGLWidget(parent), helper(helper)
{
QImage img("E:\\pictures\\0151.jpg");
image = img;
image = image.convertToFormat(QImage::Format_RGBA8888);
setFixedSize(512, 512);
setAutoFillBackground(false);
targetWidth = width();
targetHeight = height();
qDebug() << "targetWidth="<<targetWidth;
qDebug() << "targetHeight ="<<targetHeight ;
//this values i am trying to pass to my fragment shader
grid = 5.0;//grid on image
dividerValue = 0.5;
step_x = 0.0015625;
step_y = height() ? (2.5 * step_x * targetWidth / targetHeight) : 0.0;
}
void GLWidget::initializeGL()
{
initializeOpenGLFunctions();
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShader);//?
m_program->link();
m_program->bind();
m_program->release();
}
//we can use paintEvent to display our image with opengl
void GLWidget::paintEvent(QPaintEvent *event)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_program->bind();
QPainter painter;
painter.begin(this);
painter.drawImage(0,0,image);
QOpenGLTexture texture(image); //I dont know how to setUniformValue(m_program->uniformLocation("source"),texture) to my shader
GLuint m_texture;
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
glGenerateMipmap(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
//open an image
m_program->setUniformValue("grid", grid);
m_program->setUniformValue("dividerValue",dividerValue);
m_program->setUniformValue("step_x", step_x);
m_program->setUniformValue("step_y", step_y);
m_program->setUniformValue(m_program->uniformLocation("source"),m_texture);
painter.end();
m_program->release();
}
答案 0 :(得分:2)
绑定纹理时,它会绑定到当前活动的纹理图像单元(请参阅Binding textures to samplers)。
可以通过glActiveTexture
选择活动纹理单元。默认纹理单位为GL_TEXTURE0
。
您必须为纹理采样器制服提供的值不是纹理的名称,它是纹理单元(数字),纹理绑定到的位置:
int texture_unit = 0; // <----- e.g. texture unit 0
glActiveTexture( GL_TEXTURE0 + texture_unit );
glBindTexture( GL_TEXTURE_2D, m_texture );
.....
m_program->bind();
m_program->setUniformValue( "source", texture_unit ); // <----- texture unit
对于QOpenGLTexture
对象,可以通过QOpenGLTexture::bind
:
int texture_unit = 1; // <----- e.g. texture unit 1
QOpenGLTexture texture(image);
texture.bind( texture_unit );
m_program->bind();
m_program->setUniformValue( "source", texture_unit ); // <----- texture unit
注意,由于OpenGL 4.2纹理单元可以在着色器中初始化,Binding point:
layout(binding = 0) uniform sampler2D source; // binding = 0 -> texture unit 0
以下代码会将图像绘制到整个窗口小部件,并由着色器处理。最后,渲染的图像从GPU读回:
class GLWidget : public QOpenGLWidget
{
.....
QOpenGLShaderProgram * m_program = nullptr;
QOpenGLTexture * m_texture = nullptr;
};
void GLWidget::initializeGL()
{
initializeOpenGLFunctions();
QImage img("E:\\pictures\\0151.jpg");
m_texture = new QOpenGLTexture( img );
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader);
m_program->bindAttributeLocation("vPosition", 0);
m_program->link();
}
void GLWidget::paintEvent(QPaintEvent *event)
{
// celar the framebuffer
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// bind the texture
uint texture_unit = 1;
m_texture->bind( texture_unit );
// activate the shader
m_program->bind();
m_program->setUniformValue( "source", texture_unit );
m_program->setUniformValue( "grid", grid );
m_program->setUniformValue( "dividerValue", dividerValue );
m_program->setUniformValue( "step_x", step_x );
m_program->setUniformValue( "step_y", step_y );
// draw a quad over the entire widget
GLfloat vertices[]{ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
m_program->enableAttributeArray(0);
m_program->setAttributeArray(0, GL_FLOAT, vertices, 2);
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
m_program->disableAttributeArray(0);
// release the shader
m_program->release();
// read the rendered image
int width = ....;
int height = ....;
unsigned char *pixels = new unsigned char[width * height * 4];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
QImage *img = new QImage( pixels, width, height, QImage::Format_RGBA8888 );
.....
}
此外,您必须对顶点着色器和片段着色器进行一些更改。在顶点着色器中,您必须将顶点位置传递给片段着色器:
attribute vec2 vPosition;
varying vec2 v_pos;
void main()
{
v_pos = vPosition.xy;
gl_Position = vec4(vPosition.xy, 0.0, 1.0);
}
在片段着色器中,您必须从顶点位置计算文本坐标:
varying vec2 v_pos;
void main()
{
vec2 uv = v_pos.xy * 0.5 + 0.5;
....
}