链接shader,标准opengl shader处理过程
bool VideoShader::build(QOpenGLShaderProgram *shaderProgram)
{
if (shaderProgram->isLinked()) {
qWarning("Shader program is already linked");
}
shaderProgram->removeAllShaders();
shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader());
shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader());
。。。
}
vertexShader这里把模板shaders/video.vert"));, 替换掉里面的 %userHeader%*,
const char* VideoShader::vertexShader() const
{
DPTR_D(const VideoShader);
// because we have to modify the shader, and shader source must be kept, so read the origin
d.vert = shaderSourceFromFile(QStringLiteral("shaders/video.vert"));
QByteArray& vert = d.vert;
if (vert.isEmpty()) {
qWarning("Empty vertex shader!");
return 0;
}
if (textureTarget() == GL_TEXTURE_RECTANGLE && d.video_format.isPlanar()) {
vert.prepend("#define MULTI_COORD\n");
#if YUVA_DONE
if (d.video_format.hasAlpha())
vert.prepend("#define HAS_ALPHA\n");
#endif
}
vert.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Vertex));
if (userShaderHeader(QOpenGLShader::Vertex)) {
QByteArray header("*/");
header.append(userShaderHeader(QOpenGLShader::Vertex));
header += "/*";
vert.replace("%userHeader%", header);
}
#ifdef QTAV_DEBUG_GLSL
QString s(vert);
s = OpenGLHelper::removeComments(s);
qDebug() << s.toUtf8().constData();
#endif //QTAV_DEBUG_GLSL
return vert.constData();
}
fragmentShader 这里把模板shaders/planar.f.glsl, 替换掉里面的 %userHeader%*, %userSample% 和 %userPostProcess% 部分内容
const char* VideoShader::fragmentShader() const
{
DPTR_D(const VideoShader);
// because we have to modify the shader, and shader source must be kept, so read the origin
if (d.video_format.isPlanar()) {
d.planar_frag = shaderSourceFromFile(QStringLiteral("shaders/planar.f.glsl"));
} else {
d.packed_frag = shaderSourceFromFile(QStringLiteral("shaders/packed.f.glsl"));
}
QByteArray& frag = d.video_format.isPlanar() ? d.planar_frag : d.packed_frag;
if (frag.isEmpty()) {
qWarning("Empty fragment shader!");
return 0;
}
const int nb_planes = d.video_format.planeCount();
if (nb_planes == 2) //TODO: nv21 must be swapped
frag.prepend("#define IS_BIPLANE\n");
if (OpenGLHelper::hasRG() && !OpenGLHelper::useDeprecatedFormats())
frag.prepend("#define USE_RG\n");
const bool has_alpha = d.video_format.hasAlpha();
if (d.video_format.isPlanar()) {
const int bpc = d.video_format.bitsPerComponent();
if (bpc > 8) {
//// has and use 16 bit texture (r16 for example): If channel depth is 16 bit, no range convertion required. Otherwise, must convert to color.r*(2^16-1)/(2^bpc-1)
if (OpenGLHelper::depth16BitTexture() < 16 || !OpenGLHelper::has16BitTexture() || d.video_format.isBigEndian())
frag.prepend("#define CHANNEL16_TO8\n");
}
#if YUVA_DONE
if (has_alpha)
frag.prepend("#define HAS_ALPHA\n");
#endif
} else {
if (has_alpha)
frag.prepend("#define HAS_ALPHA\n");
if (d.video_format.isXYZ())
frag.prepend("#define XYZ_GAMMA\n");
}
if (d.texture_target == GL_TEXTURE_RECTANGLE) {
frag.prepend("#extension GL_ARB_texture_rectangle : enable\n"
"#define sampler2D sampler2DRect\n");
if (OpenGLHelper::GLSLVersion() < 140)
frag.prepend("#undef texture\n"
"#define texture texture2DRect\n"
);
frag.prepend("#define MULTI_COORD\n");
}
frag.prepend(OpenGLHelper::compatibleShaderHeader(QOpenGLShader::Fragment));
QByteArray header("*/");
if (userShaderHeader(QOpenGLShader::Fragment))
header += QByteArray(userShaderHeader(QOpenGLShader::Fragment));
header += "\n";
header += "uniform vec2 u_texelSize[" + QByteArray::number(nb_planes) + "];\n";
header += "uniform vec2 u_textureSize[" + QByteArray::number(nb_planes) + "];\n";
header += "/*";
frag.replace("%userHeader%", header);
if (userSample()) {
QByteArray sample_code("*/\n#define USER_SAMPLER\n");
sample_code += QByteArray(userSample());
sample_code += "/*";
frag.replace("%userSample%", sample_code);
}
if (userPostProcess()) {
QByteArray pp_code("*/");
pp_code += QByteArray(userPostProcess()); //why the content is wrong sometimes if no ctor?
pp_code += "/*";
frag.replace("%userPostProcess%", pp_code);
}
frag.replace("%planes%", QByteArray::number(nb_planes));
#ifdef QTAV_DEBUG_GLSL
QString s(frag);
s = OpenGLHelper::removeComments(s);
qDebug() << s.toUtf8().constData();
#endif //QTAV_DEBUG_GLSL
return frag.constData();//返回组装好的frag
}
VideoShader 里面可以定制的部分:
/// User configurable shader APIs BEGIN
/*!
* Keywords will be replaced in user shader code:
* %planes% => plane count
* Uniforms can be used: (N: 0 ~ planes-1)
* u_Matrix (vertex shader),
* u_TextureN, v_TexCoordsN, u_texelSize(array of vec2, normalized), u_textureSize(array of vec2), u_opacity, u_c(channel map), u_colorMatrix, u_to8(vec2, computing 16bit value with 8bit components)
* Vertex shader in: a_Position, a_TexCoordsN (see attributeNames())
* Vertex shader out: v_TexCoordsN
*/
/*!
* \brief userShaderHeader
* Must add additional uniform declarations here
*/
virtual const char* userShaderHeader(QOpenGLShader::ShaderType) const {return 0;}
/*!
* \brief setUserUniformValues
* Call program()->setUniformValue(...) here
* You can upload a texture for blending in userPostProcess(),
* or LUT texture used by userSample() or userPostProcess() etc.
* \return false if use use setUserUniformValue(Uniform& u), true if call program()->setUniformValue() here
*/
virtual bool setUserUniformValues() {return false;}
/*!
* \brief setUserUniformValue
* Update value of uniform u. Call Uniform.set(const T& value, int count); VideoShader will call Uniform.setGL() later if value is changed
*/
virtual void setUserUniformValue(Uniform&) {}
/*!
* \brief userSample
* Fragment shader only. The custom sampling function to replace texture2D()/texture() (replace %1 in shader).
* \code
* vec4 sample2d(sampler2D tex, vec2 pos, int plane) { .... }
* \endcode
* The 3rd parameter can be used to get texel/texture size of a given plane u_texelSize[plane]/textureSize[plane];
* Convolution of result rgb and kernel has the same effect as convolution of input yuv and kernel, ensured by
* Σ_i c_i* Σ_j k_j*x_j=Σ_i k_i* Σ_j c_j*x_j
* Because because the input yuv is from a real rgb color, so no clamp() is required for the transformed color.
*/
virtual const char* userSample() const { return 0;}
/*!
* \brief userPostProcess
* Fragment shader only. Process rgb color
* TODO: parameter ShaderType?
*/
virtual const char* userPostProcess() const {return 0;}
/// User configurable shader APIs END