使用PBO更新NV21纹理,shader处理并渲染到FBO中,再进行二次渲染的例子

使用PBO对比glTexImage2d函数可以提升纹理更新效率,因为glTexImage2d更新纹理需要把内存数据复制到显存,而这完全由CPU进行操作:

但使用PBO则可以让显卡驱动利用DMA通道,绕开CPU直接从指定的内存位置拷贝数据:

 

使用两个PBO则可以进行异步更新进一步提升效能,例如一个在写的同时另外一个可以读:

但PBO也仅仅是通过DMA提升纹理更新效率,因此纹理还是要创建的,我们先创建Y通道和UV通道两个只占用显存空间但内存全为0的纹理。

双PBO + 双FBO架构如下:

   使用此方式可以把YUV转换后的数据离屏渲染到FBO中,作为一整个纹理可以灵活地使用卷积核、滤镜shader等实现降噪、抖音特效等画面的二次处理。

因为Y通道和UV通道的处理方式不一样,因此创建两个空纹理,Y通道一个,UV通道一个:

    private void startBindTexture() {
        createEmptyTexture(mGenYTextureId, mImgWidth, mImgHeight, GLES30.GL_LUMINANCE);
        createEmptyTexture(mGenYTextureId, mImgWidth, mImgHeight, GLES30.GL_LUMINANCE);
    }
    private void createEmptyTexture(int textureID, int imgWidth, int imgHeight, int pixelFormat) {
        GLES30.glGenTextures(1, new int[] {textureID}, 0); //只要值不重复即可
        //UV纹理初始化
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureID);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
        //创建一个占用指定空间的纹理,但暂时不复制数据进去,等PBO进行数据传输,取代glTexImage2D,利用DMA提高数据拷贝速度
        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, pixelFormat, imgWidth, imgHeight, 0, pixelFormat, GLES30.GL_UNSIGNED_BYTE, null); //因为这里使用了双字节,所以纹理大小对比使用单字节的Y通道纹理,宽度首先要缩小一般,而uv层高度本来就只有y层一般,所以高度也除以2
    }

       其中Y通道使用单字节方式读写,也就是GL_LUMINANCE或者GL_ALPHA,UV通道为两个字节为一组,所以更适合使用两字节的模式,也就是GL_LUMINANCE_ALPHA格式,但相对地,纹理宽度就变成了纯灰度格式的一半,YUV的压缩方式也决定了每2个Y对应一个UV,因此UV层宽度也得除以2,所以,UV纹理的长宽便时mImgWidth / 2, mImgHeight / 2。

创建PBO:

    /**
     * 创建2个framebuffer作为每次渲染结果的叠加专用纹理
     **/
    private void createPBO() {
        //创建Y通道PBO
        mImgPanelYByteSize = mImgWidth * mImgHeight;
        mYPanelPixelBuffferPointerArray = new int[2];
        GLES30.glGenBuffers(2, mYPanelPixelBuffferPointerArray, 0);

        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mYPanelPixelBuffferPointerArray[0]);
        GLES30.glBufferData(GLES30.GL_PIXEL_UNPACK_BUFFER, mImgPanelYByteSize,  null, GLES30.GL_STREAM_DRAW);

        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mYPanelPixelBuffferPointerArray[1]);
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mImgPanelYByteSize,  null, GLES30.GL_STREAM_DRAW);
        //创建UV通道PBO
        mImgPanelUVByteSize = mImgWidth * mImgHeight / 2;
        mUVPanelPixelBuffferPointerArray = new int[2];
        GLES30.glGenBuffers(2, mUVPanelPixelBuffferPointerArray, 0);

        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mUVPanelPixelBuffferPointerArray[0]);
        GLES30.glBufferData(GLES30.GL_PIXEL_UNPACK_BUFFER, mImgPanelUVByteSize,  null, GLES30.GL_STREAM_DRAW);

        GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mUVPanelPixelBuffferPointerArray[1]);
        GLES30.glBufferData(GLES30.GL_PIXEL_PACK_BUFFER, mImgPanelUVByteSize,  null, GLES30.GL_STREAM_DRAW);
    }

此时PBO已创建完成。现在我们来完成纹理数据更新的方法吧:

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void refreshBuffer(byte[] imgBytes) {
        //更新ypanel数据
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mGenYTextureId);
        GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "textureY"), 0); //获取纹理属性的指针
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mYPanelPixelBuffferPointerArray[mFrameCount % 2]);
        GLES30.glTexSubImage2D(GLES30.GL_TEXTURE_2D, 0, 0, 0, mImgWidth, mImgHeight, GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, null); //1字节为一个单位
        //更新图像数据,复制到 PBO 中
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mYPanelPixelBuffferPointerArray[(mFrameCount + 1) % 2]);
        GLES30.glBufferData(GLES30.GL_PIXEL_UNPACK_BUFFER, mImgPanelYByteSize, null, GLES30.GL_STREAM_DRAW);
        Buffer buf = GLES30.glMapBufferRange(GLES30.GL_PIXEL_UNPACK_BUFFER, 0, mImgPanelYByteSize, GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);
        //填充像素
        ByteBuffer bytebuffer = ((ByteBuffer) buf).order(ByteOrder.nativeOrder());
        bytebuffer.position(0);
        bytebuffer.put(imgBytes, 0, mImgWidth * mImgHeight);
        bytebuffer.position(0);
        GLES30.glUnmapBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER);
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, 0);
        //更新uvpanel数据
        GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mGenUVTextureId);
        GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "textureUV"), 1); //获取纹理属性的指针
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mUVPanelPixelBuffferPointerArray[mFrameCount % 2]);
        GLES30.glTexSubImage2D(GLES30.GL_TEXTURE_2D, 0, 0, 0, mImgWidth / 2, mImgHeight / 2, GLES30.GL_LUMINANCE_ALPHA, GLES30.GL_UNSIGNED_BYTE, null); //2字节为一个单位,所以宽度因为单位为2字节一个,对比1字节时直接对半
        //更新图像数据,复制到 PBO 中
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, mUVPanelPixelBuffferPointerArray[(mFrameCount + 1) % 2]);
        GLES30.glBufferData(GLES30.GL_PIXEL_UNPACK_BUFFER, mImgPanelUVByteSize, null, GLES30.GL_STREAM_DRAW);
        buf = GLES30.glMapBufferRange(GLES30.GL_PIXEL_UNPACK_BUFFER, 0, mImgPanelUVByteSize, GLES30.GL_MAP_WRITE_BIT | GLES30.GL_MAP_INVALIDATE_BUFFER_BIT);
        //填充像素
        bytebuffer = ((ByteBuffer) buf).order(ByteOrder.nativeOrder());
        bytebuffer.position(0);
        bytebuffer.put(imgBytes, mImgWidth * mImgHeight, mImgWidth * mImgHeight / 2);
        bytebuffer.position(0);
        GLES30.glUnmapBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER);
        GLES30.glBindBuffer(GLES30.GL_PIXEL_UNPACK_BUFFER, 0);
    }

 原理就是把yuv数组分段利用PBO写到对应的纹理中。

编写shader,实现YUV420转换为RGBA:

//https://blog.csdn.net/byhook/article/details/84037338 有各种YUV的排列方式
/**yuvtorgb
        R = Y + 1.4075 *(V-128)
       G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
       B = Y + 1.779 *(U – 128)
**/

vec3 yuvToRGB(float y, float u, float v) {
    float r = y + 1.370705 * (v - 0.5);
    float g = y - 0.337633 * (u - 0.5) - 0.698001 * (v - 0.5);
    float b = y + 1.732446 * (u - 0.5);
    //    r = clamp(r, 0.0, 1.0);
    //    g = clamp(g, 0.0, 1.0);
    //    b = clamp(b, 0.0, 1.0);
    return vec3(r, g, b);
}

void convertYUV420SP(bool reverse, in vec2 fragVTexCoord, out vec4 fragColor){
    float y = texture(textureY, fragVTexCoord)[0];
    //uvuvuvuv
    float u = texture(textureUV, fragVTexCoord)[3];
    float v = texture(textureUV, fragVTexCoord)[0];
    vec3 rgb;
    if (reverse) {
        rgb = yuvToRGB(y, v, u);//NV21
    } else {
        rgb = yuvToRGB(y, u, v);//NV12
    }
    fragColor = vec4(rgb, 1.0);
}

创建两个FBO,刚刚YUV转换为RGBA后的画面可以渲染到这里:

    /**
     * 创建2个framebuffer保存每次渲染结果
     **/
    private void createDoubleFrameBuffer() {
        int frameBufferCount = 2;

        //生成framebuffer
        mFrameBufferPointerArray = new int[frameBufferCount];
        GLES30.glGenFramebuffers(mFrameBufferPointerArray.length, mFrameBufferPointerArray, 0);

        //生成渲染缓冲buffer
        mRenderBufferPointerArray = new int[frameBufferCount];
        GLES30.glGenRenderbuffers(mRenderBufferPointerArray.length, mRenderBufferPointerArray, 0);

        //生成framebuffer纹理pointer
        mFrameBufferTexturePointerArray = new int[frameBufferCount];
        GLES30.glGenTextures(mFrameBufferTexturePointerArray.length, mFrameBufferTexturePointerArray, 0);

        //遍历framebuffer并初始化
        for (int i = 0; i < frameBufferCount; i++) {
            //绑定帧缓冲,遍历两个framebuffer分别初始化
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferPointerArray[i]);
            //绑定缓冲pointer
            GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, mRenderBufferPointerArray[i]);
            //为渲染缓冲初始化存储,分配显存
            GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER,
                    GLES30.GL_DEPTH_COMPONENT16, mFrameBufferWidth, mFrameBufferHeight); //设置framebuffer的长宽

            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFrameBufferTexturePointerArray[i]); //绑定纹理Pointer

            GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,//设置MIN采样方式
                    GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
            GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,//设置MAG采样方式
                    GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
            GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,//设置S轴拉伸方式
                    GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
            GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,//设置T轴拉伸方式
                    GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
            GLES30.glTexImage2D//设置颜色附件纹理图的格式
                    (
                            GLES30.GL_TEXTURE_2D,
                            0,                        //层次
                            GLES30.GL_RGBA,        //内部格式
                            mFrameBufferWidth,            //宽度
                            mFrameBufferHeight,            //高度
                            0,                        //边界宽度
                            GLES30.GL_RGBA,            //格式
                            GLES30.GL_UNSIGNED_BYTE,//每个像素数据格式
                            null
                    );
            GLES30.glFramebufferTexture2D        //设置自定义帧缓冲的颜色缓冲附件
                    (
                            GLES30.GL_FRAMEBUFFER,
                            GLES30.GL_COLOR_ATTACHMENT0,    //颜色缓冲附件
                            GLES30.GL_TEXTURE_2D,
                            mFrameBufferTexturePointerArray[i],                        //纹理id
                            0                                //层次
                    );
            GLES30.glFramebufferRenderbuffer    //设置自定义帧缓冲的深度缓冲附件
                    (
                            GLES30.GL_FRAMEBUFFER,
                            GLES30.GL_DEPTH_ATTACHMENT,        //深度缓冲附件
                            GLES30.GL_RENDERBUFFER,            //渲染缓冲
                            mRenderBufferPointerArray[i]                //渲染深度缓冲id
                    );
        }
        //绑回系统默认framebuffer,否则会显示不出东西
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);//绑定帧缓冲id
    }

绑定Y通道纹理和UV通道纹理,绘制纹理到FBO中,绘制的同时YUV会被转换为色彩正常的RGBA颜色:

    /**绘制画面到framebuffer**/
    private void drawToFrameBuffer(float[] cameraMatrix, float[] projMatrix) {
        if (mIsDestroyed) {
            return;
        }
        GLES30.glUseProgram(mYuvBufferDrawProgram);
        //设置视窗大小及位置
        GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight);
        //绑定帧缓冲id
        if (mFrameCount % 2 == 0) {
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferPointerArray[0]);
        } else {
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferPointerArray[1]);
        }
        //清除深度缓冲与颜色缓冲
        if (!mFrameBufferClean && !mFrameBufferCleanOnce) { //实现渲染画面叠加
            GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
            mFrameBufferCleanOnce = true;
        }
        if (mFrameBufferClean) {
            GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
            GLES30.glUniform1i(mGLFrameBufferProgramFunChoicePointer, -1); //第一次加载选择纹理方式渲染
        }
        //设置它的坐标系
        locationTrans(cameraMatrix, projMatrix, this.mGLFrameuMVPMatrixPointer);
        //设置图像分辨率
        GLES30.glUniform2fv(mResoulutionPointer, 1, new float[]{mWindowW, mWindowH}, 0);
        locationTrans(cameraMatrix, projMatrix, this.mGLFrameuMVPMatrixPointer);
        if (mPointBuf != null && mColorBuf != null) {
            if (mFrameCount < 0) {
                mFrameCount = 0;
            }
            mPointBuf.position(0);
            mColorBuf.position(0);
//            GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "sTexture"), 0); //获取纹理属性的指针
            //将顶点位置数据送入渲染管线
            GLES30.glVertexAttribPointer(mGLFrameObjectPositionPointer, 3, GLES30.GL_FLOAT, false, 0, mPointBuf); //三维向量,size为2
            //将顶点颜色数据送入渲染管线
            GLES30.glVertexAttribPointer(mGLFrameObjectVertColorArrayPointer, 4, GLES30.GL_FLOAT, false, 0, mColorBuf);
            //将顶点纹理坐标数据传送进渲染管线
            GLES30.glVertexAttribPointer(mGLFrameVTexCoordPointer, 2, GLES30.GL_FLOAT, false, 0, mTexCoorBuffer);  //二维向量,size为2
            GLES30.glEnableVertexAttribArray(mGLFrameObjectPositionPointer); //启用顶点属性
            GLES30.glEnableVertexAttribArray(mGLFrameObjectVertColorArrayPointer);  //启用颜色属性
            GLES30.glEnableVertexAttribArray(mGLFrameVTexCoordPointer);  //启用纹理采样定位坐标
//            GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
            //绘制yuv

            GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mGenYTextureId);
            GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "textureY"), 0); //获取纹理属性的指针

            GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mGenUVTextureId);
            GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "textureUV"), 1); //获取纹理属性的指针

            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mPointBufferPos / 3); //绘制线条,添加的point浮点数/3才是坐标数(因为一个坐标由x,y,z3个float构成,不能直接用)
            GLES30.glDisableVertexAttribArray(mGLFrameObjectPositionPointer);
            GLES30.glDisableVertexAttribArray(mGLFrameObjectVertColorArrayPointer);
            GLES30.glDisableVertexAttribArray(mGLFrameVTexCoordPointer);
        }
        //绑会系统默认framebuffer,否则会显示不出东西
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);//绑定帧缓冲id
    }

此时画面已离屏渲染到FBO中,现在我们把FBO画面在shader中进行边缘锐化卷积操作,并最后绑定当前窗口的framebuffer——也就是0号framebuffer,然后纹理进行bindTexuter后,显示出来。

shader处理程序(resolution为屏幕分辨率uniform变量):

void kernalEffect(vec2 TexCoords)
{
    float offset = 1.0 / resolution.y;
    vec2 offsets[9] = vec2[](
    vec2(-offset, offset), // 左上
    vec2(0.0, offset), // 正上
    vec2(offset, offset), // 右上
    vec2(-offset, 0.0), // 左
    vec2(0.0, 0.0), // 中
    vec2(offset, 0.0), // 右
    vec2(-offset, -offset), // 左下
    vec2(0.0, -offset), // 正下
    vec2(offset, -offset)// 右下
    );


    vec3 sampleTex[9];
    for (int i = 0; i < 9; i++)
    {
        sampleTex[i] = vec3(texture(textureFBO, TexCoords.st + offsets[i]));
    }
    vec3 col = vec3(0.0);
    for (int i = 0; i < 9; i++)
    col += sampleTex[i] * kernel[i];

    fragColor = vec4(col, 1.0);
}

绘制FBO到窗口的代码:

@Override
    public void drawTo(float[] cameraMatrix, float[] projMatrix) {
        if (mIsDestroyed) {
            return;
        }
        locationTrans(cameraMatrix, projMatrix, mGLFrameuMVPMatrixPointer);
        //先绘制内容到frambuffer
        drawToFrameBuffer(cameraMatrix, projMatrix);
        //绘制
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);//绑定帧系统默认缓冲id
        locationTrans(cameraMatrix, projMatrix, mGLFrameuMVPMatrixPointer);

        if (mPointBuf != null && mColorBuf != null) {
            Log.i("cjztest", "drawFramebuffer");
            GLES30.glUniform1i(mGLFrameBufferProgramFunChoicePointer, -1); //选择纹理方式渲染
            mPointBuf.position(0);
            mColorBuf.position(0);
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
            //切换纹理到当前正在绘制的framebuffer
            if (mFrameCount % 2 == 1) { //另外一个当前不需要投上屏幕的FBO可以用于输出处理结果。
                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFrameBufferTexturePointerArray[0]);
            } else {
                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFrameBufferTexturePointerArray[1]);
            }
            GLES30.glUniform1i(GLES30.glGetUniformLocation(mYuvBufferDrawProgram, "textureFBO"), 0); //获取纹理属性的指针
            //将顶点位置数据送入渲染管线
            GLES30.glVertexAttribPointer(mGLFrameObjectPositionPointer, 3, GLES30.GL_FLOAT, false, 0, mPointBuf); //三维向量,size为2
            //将顶点颜色数据送入渲染管线
            GLES30.glVertexAttribPointer(mGLFrameObjectVertColorArrayPointer, 4, GLES30.GL_FLOAT, false, 0, mColorBuf);
            //将顶点纹理坐标数据传送进渲染管线
            GLES30.glVertexAttribPointer(mGLFrameVTexCoordPointer, 2, GLES30.GL_FLOAT, false, 0, mTexCoorBuffer);  //二维向量,size为2
            GLES30.glEnableVertexAttribArray(mGLFrameObjectPositionPointer); //启用顶点属性
            GLES30.glEnableVertexAttribArray(mGLFrameObjectVertColorArrayPointer);  //启用颜色属性
            GLES30.glEnableVertexAttribArray(mGLFrameVTexCoordPointer);  //启用纹理采样定位坐标
            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mPointBufferPos / 3); //绘制线条,添加的point浮点数/3才是坐标数(因为一个坐标由x,y,z3个float构成,不能直接用)
            GLES30.glDisableVertexAttribArray(mGLFrameObjectPositionPointer);
            GLES30.glDisableVertexAttribArray(mGLFrameObjectVertColorArrayPointer);
            GLES30.glDisableVertexAttribArray(mGLFrameVTexCoordPointer);
        }
        GLES30.glUniform1i(mFrameCountPointer, mFrameCount++);

    }

最后效果如下:

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL PBOOpenGL的一个扩展,它允许通过在图形处理单元(GPU)上创建一个像素缓冲对象(PBO),将数据从CPU传输到GPU,然后可以使用这些数据在纹理显示图像。 在使用PBO和YUV420p格式的纹理显示图像之前,我们需要将YUV420p格式的图像数据转换为适用于OpenGL纹理的格式。YUV420p是一种常见的视频图像格式,它包含一个与图像分辨率相同的Y分量(明亮度)和两个与图像分辨率的四分之一相同的UV分量(色度)。Y分量与图像分辨率相同,而UV分量的分辨率被降低以节省存储和传输带宽。 首先,我们需要创建一个纹理,并将它与PBO关联。然后,我们可以将YUV420p数据传输到PBO。将数据传输到PBO的过程涉及到将Y、U和V分量的数据按照特定的布局传输到PBO。我们可以使用glBufferData函数将数据传输到PBO。 接下来,我们需要将PBO的数据绑定到纹理,并对纹理进行设置以正确地显示图像。我们可以使用glBindTexture函数来绑定纹理,并使用glTexSubImage2D函数将PBO的数据传输到纹理。 最后,我们可以使用OpenGL渲染管线将纹理的图像显示在屏幕上。我们可以使用一个简单的顶点着色器和一个片段着色器将纹理的图像转换为可视化的图像。 总结起来,使用OpenGL PBO和YUV420p纹理可以更高效地显示图像。通过将图像数据传输到PBO,并将PBO纹理关联,可以在GPU上进行图像处理渲染,从而提高了图像显示的效率和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值