OpenGL.Shader:Zhigeは、ライブフィルタークライアントの作成方法を教えています(5)
最後の章では、 nv21ストリームをレンダリングする際にシームレスにフィルタを切り替える方法を紹介この章では、3つのフィルタ効果を導入し、前の章を、次のとおりです。これに対し、露出、モザイク、そしてどのように動的にフィルタ効果を調整することを。ナンセンスな話をしないで、コードを見せてください!
フィルター1:コントラスト
#include "GpuBaseFilter.hpp"
/**
* 更改图像的对比度。
* 对比度值在0.0到4.0之间,正常值为1.0
*/
class GpuContrastFilter : public GpuBaseFilter {
public:
int getTypeId() { return FILTER_TYPE_CONTRAST; }
GpuContrastFilter()
{
CONTRAST_FRAGMENT_SHADER ="precision mediump float;\n\
varying highp vec2 textureCoordinate;\n\
uniform sampler2D SamplerRGB;\n\
uniform sampler2D SamplerY;\n\
uniform sampler2D SamplerU;\n\
uniform sampler2D SamplerV;\n\
uniform lowp float contrast;\n\
mat3 colorConversionMatrix = mat3(\n\
1.0, 1.0, 1.0,\n\
0.0, -0.39465, 2.03211,\n\
1.13983, -0.58060, 0.0);\n\
vec3 yuv2rgb(vec2 pos)\n\
{\n\
vec3 yuv;\n\
yuv.x = texture2D(SamplerY, pos).r;\n\
yuv.y = texture2D(SamplerU, pos).r - 0.5;\n\
yuv.z = texture2D(SamplerV, pos).r - 0.5;\n\
return colorConversionMatrix * yuv;\n\
}\n\
void main()\n\
{\n\
vec4 textureColor = vec4(yuv2rgb(textureCoordinate), 1.0);\n\
gl_FragColor = vec4((contrast*(textureColor.rgb - vec3(0.5)) + vec3(0.5)), textureColor.w);\n\
}";
}
~GpuContrastFilter() {
if(!CONTRAST_FRAGMENT_SHADER.empty()) CONTRAST_FRAGMENT_SHADER.clear();
}
void init() {
GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), CONTRAST_FRAGMENT_SHADER.c_str());
mContrastLocation = glGetUniformLocation(mGLProgId, "contrast");
mContrastValue = 1.0f;
}
void setAdjustEffect(float percent) {
mContrastValue = percent * 4.0f;
} // (经验范围控制在0~4)
void onDraw(GLuint SamplerY_texId, GLuint SamplerU_texId, GLuint SamplerV_texId,
void* positionCords, void* textureCords)
{ // ...}
private:
std::string CONTRAST_FRAGMENT_SHADER;
GLint mContrastLocation;
float mContrastValue;
};
頂点シェーダーは基本クラスGpuBaseFilter.NO_FILTER_VERTEX_SHADERを使用し、フラグメントシェーダーの波を分析します。コントラストの原理を見つけることは難しくありません。
gl_FragColor = vec4((contrast*(textureColor.rgb - vec3(0.5)) + vec3(0.5)), textureColor.w);
textureColor.rgb-vec3(0.5)は、バイナリアライメントに使用されます。これは、量子化としても理解できます。コントラストはコントラスト係数です。コントラストを掛けると、さまざまな色の値のレベル範囲が拡張されます。(体験範囲は0〜4でコントロール)
明らかに、このコントラストは動的に調整する必要があります。前の章の設計方法を思い出して、GpuFilterRenderでsetAdjustEffect(floatpercent)を追跡すると、renderOnDrawでも呼び出されていることを見つけるのは難しくありません。コードの一部は次のとおりです。
void GpuFilterRender::renderOnDraw(double elpasedInMilliSec)
{
// 画面渲染
mWindowSurface->makeCurrent();
yTextureId = updateTexture(dst_y, yTextureId, mFrameWidth, mFrameHeight);
uTextureId = updateTexture(dst_u, uTextureId, mFrameWidth/2, mFrameHeight/2);
vTextureId = updateTexture(dst_v, vTextureId, mFrameWidth/2, mFrameHeight/2);
// 检测更新Filter
checkFilterChange();
if( mFilter!=NULL) {
mFilter->setAdjustEffect(mFilterEffectPercent);
mFilter->onDraw(yTextureId, uTextureId, vTextureId, positionCords, textureCords);
}
// ...
}
void GpuFilterRender::adjustFilterValue(int value, int max) {
mFilterEffectPercent = (float)value / (float)max;
//LOGD("GpuFilterRender adjust %f", mFilterEffectPercent);
}
///gpu_filter_jni//
JNIEXPORT void JNICALL
Java_org_zzrblog_gpufilter_GpuFilterRender_adjustFilterValue(JNIEnv *env, jobject instance, jint value, jint max) {
if (render == NULL)
render = new GpuFilterRender();
render->adjustFilterValue(value, max);
}
トレースバックを続けると、SeekbarがActivityを介して呼び出され、次にCfeScheduler.adjustFilterValue(value、max)が呼び出され、最後にコントラスト係数のコントラストが動的に調整されていることがわかります。効果は次のとおりです。
フィルター2:露出(黒と白の逆)
#include "GpuBaseFilter.hpp"
/**
* 反转图像中的所有颜色。
*/
class GpuColorInvertFilter : public GpuBaseFilter {
public:
int getTypeId() { return FILTER_TYPE_COLOR_INVERT; }
GpuColorInvertFilter()
{
COLOR_INVERT_FRAGMENT_SHADER="precision mediump float;\n\
varying highp vec2 textureCoordinate;\n\
uniform sampler2D SamplerRGB;\n\
uniform sampler2D SamplerY;\n\
uniform sampler2D SamplerU;\n\
uniform sampler2D SamplerV;\n\
mat3 colorConversionMatrix = mat3(\n\
1.0, 1.0, 1.0,\n\
0.0, -0.39465, 2.03211,\n\
1.13983, -0.58060, 0.0);\n\
vec3 yuv2rgb(vec2 pos)\n\
{\n\
vec3 yuv;\n\
yuv.x = texture2D(SamplerY, pos).r;\n\
yuv.y = texture2D(SamplerU, pos).r - 0.5;\n\
yuv.z = texture2D(SamplerV, pos).r - 0.5;\n\
return colorConversionMatrix * yuv;\n\
}\n\
void main()\n\
{\n\
vec4 textureColor = vec4(yuv2rgb(textureCoordinate), 1.0);\n\
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);\n\
}";
}
~GpuColorInvertFilter() {
if(!COLOR_INVERT_FRAGMENT_SHADER.empty()) COLOR_INVERT_FRAGMENT_SHADER.clear();
}
void init() {
GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), COLOR_INVERT_FRAGMENT_SHADER.c_str());
}
private:
std::string COLOR_INVERT_FRAGMENT_SHADER;
};
頂点シェーダーは引き続き基本クラスGpuBaseFilter.NO_FILTER_VERTEX_SHADERを使用してフラグメントシェーダーの波を分析し、露出の原理を見つけることは難しくありません。
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
実際、それは否定です!そして、動的調整の必要がないので、簡単です(piece_piece)
最後のレンダリングの意味:
フィルター3:モザイク
最後の最も興味深いフィルター効果である古いドライバーは、最も厄介なものかもしれません(squinting smile.jpg)
#include "GpuBaseFilter.hpp"
/**
* 对图像应用格仔化效果。
*/
class GpuPixelationFilter : public GpuBaseFilter {
public:
int getTypeId() { return FILTER_TYPE_PIXELATION; }
GpuPixelationFilter()
{
PIXELATION_FRAGMENT_SHADER="precision highp float;\n\
varying highp vec2 textureCoordinate;\n\
uniform sampler2D SamplerRGB;\n\
uniform sampler2D SamplerY;\n\
uniform sampler2D SamplerU;\n\
uniform sampler2D SamplerV;\n\
mat3 colorConversionMatrix = mat3(\n\
1.0, 1.0, 1.0,\n\
0.0, -0.39465, 2.03211,\n\
1.13983, -0.58060, 0.0);\n\
uniform float imageWidthFactor;\n\
uniform float imageHeightFactor;\n\
uniform float pixel;\n\
vec3 yuv2rgb(vec2 pos)\n\
{\n\
vec3 yuv;\n\
yuv.x = texture2D(SamplerY, pos).r;\n\
yuv.y = texture2D(SamplerU, pos).r-0.5;\n\
yuv.z = texture2D(SamplerV, pos).r-0.5;\n\
return colorConversionMatrix * yuv;\n\
}\n\
void main()\n\
{\n\
vec2 uv = textureCoordinate.xy;\n\
float dx = pixel * imageWidthFactor;\n\
float dy = pixel * imageHeightFactor;\n\
vec2 coord = vec2(dx*floor(uv.x / dx), dy*floor(uv.y / dy));\n\
gl_FragColor = vec4(yuv2rgb(coord), 1.0);\n\
}";
}
~GpuPixelationFilter() {
if(!PIXELATION_FRAGMENT_SHADER.empty()) PIXELATION_FRAGMENT_SHADER.clear();
}
void init() {
GpuBaseFilter::init(NO_FILTER_VERTEX_SHADER.c_str(), PIXELATION_FRAGMENT_SHADER.c_str());
mPixelLocation = glGetUniformLocation(mGLProgId, "pixel");
mImageWidthFactorLocation = glGetUniformLocation(mGLProgId, "imageWidthFactor");
mImageHeightFactorLocation = glGetUniformLocation(mGLProgId, "imageHeightFactor");
mPixelValue = 1.0f;
}
void setAdjustEffect(float percent) {
if(percent==0.0f) percent=0.01f;
mPixelValue = percent * 100.0f;
}
void onOutputSizeChanged(int width, int height) {
GpuBaseFilter::onOutputSizeChanged(width, height);
glUniform1f(mImageWidthFactorLocation, 1.0f / width);
glUniform1f(mImageHeightFactorLocation, 1.0f / height);
}
// ...
void onDraw(GLuint SamplerY_texId, GLuint SamplerU_texId, GLuint SamplerV_texId,
void* positionCords, void* textureCords)
{
if (!mIsInitialized)
return;
glUseProgram(mGLProgId);
glUniform1f(mPixelLocation, mPixelValue);
glUniform1f(mImageWidthFactorLocation, 1.0f / mOutputWidth);
glUniform1f(mImageHeightFactorLocation, 1.0f / mOutputHeight);
// 绘制的模板代码,此处省略
}
};
たくさんのコンテンツがあります。一緒に波を分析しましょう。
uniform float imageWidthFactor; // 当前屏幕宽度因子,取值为当前宽度的1/10 uniform float imageHeightFactor; uniform float pixel; // 采样跨度 void main() { vec2 uv = textureCoordinate.xy; // 当前纹理坐标 float dx = pixel * imageWidthFactor; // 根据采样跨度,调整步长 float dy = pixel * imageHeightFactor; // floor(uv.x / dx)“向下舍入”,具体数值说明白,屏幕720*1280,widthFacetor=72 // uv.x = 1,pixel = 1,代入计算 72*(floor(1/72)) = 0 // uv.x = 2,pixel = 1,代入计算 72*(floor(2/72)) = 0 // uv.x = 71,pixel = 1,代入计算 72*(floor(71/72)) = 0 // uv.x = 72,pixel = 1,代入计算 72*(floor(72/72)) = 72 //以上可以说明,通过计算,可以把步长范围内的所有纹理坐标,锁定到范围内的第一个像素坐标位置进行纹理采样。 vec2 coord = vec2(dx*floor(uv.x / dx), dy*floor(uv.y / dy)); gl_FragColor = vec4(yuv2rgb(coord), 1.0); }";
分析が完了したら、効果図を見てください。
プロジェクトアドレス:https: //github.com/MrZhaozhirong/NativeCppApp シェーダーはsrc \ main \ cpp \ gpufilter \ filterの中央に配置されます