【Android车载系列】第13章 车载渲染-OpenGL实现屏幕渲染

news2025/1/4 15:15:07

1 OpenGL渲染

上一章节我们通过SurfaceFlinger拿到Surface进行图像绘制,这节课我们通过GLSurfaceView来进行绘制,把摄像头的数据采集后展示渲染在屏幕上,这种方式是在GPU进行处理和绘制。

1.1 渲染使用GLSurfaceView

自定义CarView继承GLSurfaceView
CarView.java

package com.example.carscreen;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;

import androidx.camera.core.Preview;
import androidx.lifecycle.LifecycleOwner;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class CarView
        extends GLSurfaceView
        implements GLSurfaceView.Renderer, Preview.OnPreviewOutputUpdateListener {

    ScreenFilter screenFilter;

    private SurfaceTexture mCameraTexture;

    /**
     * 在GPU的位置(第几个图层,共32个图层随便传一个都行)
     */
    private int textures = 0;

    public CarView(Context context) {
        super(context);
    }

    public CarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        CameraHelper cameraHelper = new CameraHelper((LifecycleOwner) getContext(), this);
        // 设置稳定版:2
        setEGLContextClientVersion(2);
        setRenderer(this);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        // 图像,图层
        mCameraTexture.attachToGLContext(textures);
        // 数据过来,我们去onFrameAvailable
        mCameraTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
            @Override
            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                // 调用了requestRender()相当于View的invilidate()
                // onDrawFrame()相当于onDraw()
                // requestRender()被调用后,onDrawFrame()方法被调用
                requestRender();
            }
        });

        screenFilter = new ScreenFilter(getContext());
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int i, int i1) {

    }

    /**
     * 这个渲染方法会不断的被回调:
     * 1.手动触发(调用requestRender()方法)
     * 2.被动触发(16ms一次)
     *
     * @param gl10
     */
    @Override
    public void onDrawFrame(GL10 gl10) {
        Log.d("yvan", "---->1");
        // 获取最新camera数据
        mCameraTexture.updateTexImage();
        screenFilter.onDraw(getWidth(), getHeight(), textures);
    }

    /**
     * 摄像头有画面进来,就会被调用。
     * 约20-30帧/s,跟摄像头有关系(设置了输出就会被调用)
     *
     * @param output
     */
    @Override
    public void onUpdated(Preview.PreviewOutput output) {
        // 数据源
        mCameraTexture = output.getSurfaceTexture();
    }
}

1.2 Camera2展示摄像头采集数据

在项目的build.gradle添加Camera2库,用于展示摄像头采集的数据:

dependencies {
...
    implementation "androidx.camera:camera-core:1.0.0-alpha05"
    implementation "androidx.camera:camera-camera2:1.0.0-alpha05"
}

下面我们创建一个Camera管理类。CameraX.bindToLifecycle绑定预览,Preview数据在GPU, YUV在CPU。

CameraHelper.java

package com.example.carscreen;

import android.util.Size;

import androidx.camera.core.CameraX;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
import androidx.lifecycle.LifecycleOwner;

public class CameraHelper {

    private Preview.OnPreviewOutputUpdateListener listener;

    public CameraHelper(LifecycleOwner lifecycleOwner, Preview.OnPreviewOutputUpdateListener listener) {
        this.listener = listener;
        // 绑定预览
        CameraX.bindToLifecycle(lifecycleOwner, getPreView());
    }

    private Preview getPreView() {
        // 设置配置
        PreviewConfig previewConfig = new PreviewConfig.Builder()
                // 大小
                .setTargetResolution(new Size(640, 480))
                // 后置摄像头
                .setLensFacing(CameraX.LensFacing.BACK)
                .build();
        Preview preview = new Preview(previewConfig);
        // 监听摄像头更新
        preview.setOnPreviewOutputUpdateListener(listener);
        return preview;
    }
}

1.3 片元程序和顶点程序

下面我们在raw文件夹中创建两个文件:片元和顶点
camera_frag.glsl

#extension GL_OES_EGL_image_external : require
precision lowp float;
//接受的坐标
varying vec2 aCoord;
//[]  对象 片元
uniform samplerExternalOES vTexture;
// 顶点 形状     这么确定形状
void main() {

    float y= aCoord.y;
    if(y<0.5)
    {
        y+=0.25;
    }else{
        y -= 0.25;
    }
    gl_FragColor= texture2D(vTexture, vec2( y,aCoord.x));

//    vec4 src = texture2D(vTexture, aCoord);
//    gl_FragColor = src;
}

camera_vert.glsl

//确定形状vec4   vec

attribute  vec4 vPosition;

//android坐标系  屏幕
attribute vec4 vCoord;

//怎么输出
varying vec2 aCoord;

//vec4  =  [{-1,1},{-1,1},{-1,1},{-1,1},]
void main() {
//    GPU程序内置的系统变量     GPU  矩形
    gl_Position = vPosition;
    aCoord=vCoord.xy;
}

1.4 加载编译片元程序和顶点程序

然后开启读取片元和顶点文件,用于OpenGL的渲染需要进行散步走
1.创建片元/顶点程序
2.加载片元/顶点程序
3.编译

ScreenFilter.java

package com.example.carscreen;

import android.content.Context;
import android.opengl.GLES20;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

public class ScreenFilter {

    FloatBuffer vertexBuffer;
    FloatBuffer textureBuffer;

    int vTexture;

    //4 *2 * 4  视频播放   opengl     视频   旋转   gpu
    float[] VERTEX = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f
    };
    float[] TEXTURE = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f
    };

    int program;
    int vPosition;

    int vCoord;
//   String gVertexShader =
//            "attribute vec4 vPosition;\n"+
//            "void main() {\n"+
//            "  gl_Position = vPosition;\n"+
//            "}\n";
  字符串
//String gFragmentShader =
//            "precision mediump float;\n"+
//            "void main() {\n"+
//            "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"+
//            "}\n";


    // 准备工作
    public ScreenFilter(Context context) {
        // 对象数组作为入参、出参
        int[] status = new int[1];
        
        // 1.顶点程序
        String vertexSharder = readRawTextFile(context, R.raw.camera_vert);
        int vShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        //加载 顶点程序代码
        GLES20.glShaderSource(vShader, vertexSharder);
        //编译(配置)
        GLES20.glCompileShader(vShader);
        //查看配置 是否成功
        GLES20.glGetShaderiv(vShader, GLES20.GL_COMPILE_STATUS, status, 0);
        if (status[0] != GLES20.GL_TRUE) {
            //失败
            throw new IllegalStateException("load vertex shader:" + GLES20.glGetShaderInfoLog
                    (vShader));
        }

        // 2.片元程序
        String fragSharder = readRawTextFile(context, R.raw.camera_frag);
        int fShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        //加载着色器代码
        GLES20.glShaderSource(fShader, fragSharder);
        //编译(配置)
        GLES20.glCompileShader(fShader);
        //查看配置 是否成功
        GLES20.glGetShaderiv(fShader, GLES20.GL_COMPILE_STATUS, status, 0);
        if (status[0] != GLES20.GL_TRUE) {
            //失败
            throw new IllegalStateException("load fragment shader:" + GLES20.glGetShaderInfoLog
                    (vShader));
        }
        
        program = GLES20.glCreateProgram();
        // 执行 这个程序 加载顶点程序和片元程序
        GLES20.glAttachShader(program, vShader);
        GLES20.glAttachShader(program, fShader);
        //链接着色器程序
        GLES20.glLinkProgram(program);

        // 在CPU中定位到GPU中,变量的位置
        vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        vCoord = GLES20.glGetAttribLocation(program, "vCoord");
        // 采样点的坐标
        vTexture = GLES20.glGetUniformLocation(program, "vTexture");
        // 开辟传送通道
        vertexBuffer = ByteBuffer.allocateDirect(4 * 4 * 2).
                order(ByteOrder.nativeOrder()).asFloatBuffer();
        vertexBuffer.clear();
        // 把数据放进去   还没有穿过去
        vertexBuffer.put(VERTEX);

        textureBuffer = ByteBuffer.allocateDirect(4 * 4 * 2).order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        textureBuffer.clear();
        textureBuffer.put(TEXTURE);
    }


    // texture  数据源的地方
    public void onDraw(int mWidth, int mHeight, int texture) {
        GLES20.glViewport(0, 0, mWidth, mHeight);
        GLES20.glUseProgram(program);

        // 起始位置 0
        vertexBuffer.position(0);
        textureBuffer.position(0);

        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT,
                false, 0, vertexBuffer);
        // 激活变量
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT,
                false, 0, textureBuffer);
        GLES20.glEnableVertexAttribArray(vCoord);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        // texture图层
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
        // 激活的意思
        GLES20.glUniform1i(vTexture, 0);
        // 通知gpu绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }

    public String readRawTextFile(Context context, int rawId) {
        InputStream is = context.getResources().openRawResource(rawId);
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line;
        StringBuilder sb = new StringBuilder();
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

}

1.5 页面展示

最后我们展示在页面中
MainActivity.java

package com.example.carscreen;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        checkPermission();
    }
    public boolean checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA
            }, 1);
        }
        return false;
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.carscreen.CarView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.CAMERA"/>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.carscreen"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

2 CPU和GPU区别

上面我们提到CPU和GPU,那么CPU和GPU分别是什么?我们来大概了解一下:

  1. GPU:计算量大,但没什么技术含量,而且要重复很多很多次。就像你有个工作需要算几亿次一百以内加减乘除一样,最好的办法就是雇上几十个小学生一起算,一人算一部分,反正这些计算也没什么技术含量,纯粹体力活而已。
  2. CPU:像老教授,积分微分都会算,就是工资高,一个老教授资顶二十个小学生,你要是富士康你雇哪个?GPU就是这样,用很多简单的计算单元去完成大量的计算任务,纯粹的人海战术。

在这里插入图片描述
在这里插入图片描述
对比CPU和GPU上面的图片,我们可以知道GPU中ALU(运算能力)比较多,可计算简单的算术,运算快,适合密码学、挖矿、图形学等需要并行计算,无依赖性、互相独立的场合;CPU计算复杂的算术,运算慢,适合武器装备、信息化等需要复杂逻辑控制的场合;

3 总结

本章使用OpenGL实现了摄像头数据采集和渲染在屏幕上,也大概了解了一下CPU和GPU相关的知识,目前只是实现了OpenGL渲染部分,车载智能坐舱分屏显示可通过该方案加入分屏显示来实现。项目地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/515447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

考研数学武忠祥 高等数学0基础课笔记 函数和映射

常见的函数 取整函数的基本性质 函数的有界性 例题 sinx 是从-1到1的&#xff0c;但是x是无界的 遇到这种带sin的&#xff0c;就要试着取特殊值&#xff0c;让它为1或者为0 函数的单调性 函数的奇偶性 函数的周期性 举例 数学中Q表示有理数集&#xff0c;下面那个符号表示…

Linux 部署 Nexus (下载、安装、使用)

----仅供学习 如有侵权 联系删除----- 1、下载 下载方式1&#xff1a;官网下载 //建议用迅雷 https://help.sonatype.com/repomanager3/product-information/download/download-archives—repository-manager-3下载方式2&#xff1a;百度云盘下载 文件名&#xff1a;nexus-3…

一个让阿里面试官都说好软件测试简历模板

作为软件测试的垂直领域深耕者&#xff0c;面试或者被面试都是常有的事&#xff0c;可是不管是啥&#xff0c;总和简历有着理不清的关系&#xff0c;面试官要通过简历了解面试者的基本信息、过往经历等&#xff0c;面试者希望通过简历把自己最好的一面体现给面试官&#xff0c;…

数据库表设计规范—三范式、反范式

1.第一范式&#xff1a; 表中的属性不可分割 改为&#xff1a; 2.第二范式&#xff1a; 非主属性必须完全依赖主属性&#xff0c;不能部分依赖&#xff0c;比如只依赖联合主键中的其中一个主键就能拿到数据&#xff0c;这是不符合第二范式的 3.第三范式&#xff1a; 非主…

【Linux】缓存数据库Memcached、Memcached 安装、Memcached应用实例配置

一、 什么是缓存 缓存是指可以进行高速数据交换的存储器&#xff0c;它先于内存与CPU交换数据&#xff0c;因此速率很快。 从性能分析&#xff1a; CPU缓存>内存>磁盘>数据库 从性能来看内存是介于CPU和磁盘&#xff0c;在实际中内存是CPU和磁盘的桥梁。buffer和cache…

一、 JSP01 初识动态网页

一、 JSP01 初识动态网页 1.1 Web 应用程序开发 1.1.1 C/S 架构 客户端&#xff08;Client&#xff09;/服务器&#xff08;Server&#xff09;架构&#xff08;即 C/S 架构&#xff09;的系统主其分为客户端和用户端两层用户需要在本地安装客户端软件&#xff0c;通过网络与…

白话文讲计算机视觉-第十一讲-Harris算子

Moravec算子 说白了就是求两个像素点之间的差&#xff0c;然后平方一下给它变成正值。 其中&#xff0c;x&#xff0c;y表示像素点&#xff0c;u、v表示水平竖直方向的偏移量&#xff1b;w(x,y)为滤波函数&#xff0c;一般直接等于常数1。 I(xu,xv)、I(x,y )表示像素点(xu,xv)、…

基于zemax的折叠光路的激光扩束系统设计

激光扩束系统是激光干涉仪、激光测距仪、激光雷达等诸多仪器设备的重要组成部分&#xff0c;其光学系统多采用通过倒置的望远系统&#xff0c;来实现对激光的扩束&#xff0c;其主要作用是压缩激光束的空间发散角&#xff0c;使扩束后的激光束口径满足其他系统的要求。 激光器…

MySQL-图形化界面工具 (上)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

【sorting+双指针+数学】CF845div2 C. Quiz Master

和我一开始想的不太一样 一开始想的也是排序&#xff0c;然后双指针&#xff0c;但是我想的双指针是l1,rn的&#xff0c;因为我没注意到极差尽可能小这个条件可以转化为区间长度最短 其实就是尺取法&#xff0c;然后合法性就是这个区间内的数的所有因子能填满1~m这些格子 找…

Vue2 Vue3 Scoped 样式穿透

概念 主要是用于修改很多 Vue 常用的组件库&#xff08;Element, Vant, AntDesigin&#xff09;&#xff0c;虽然配好了样式但是还是需要更改其他的样式&#xff0c; 因为添加了 scoped 实现 css 模块化 就需要用到 样式穿透 &#xff0c;更改组件的样式 scoped 的原理 Vue …

route详解

一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡&#xff0c;让我们一起学习route。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连) 二、什么是route Route就是用来显示、人工添加和修改路由表项目的。大多数主机一般都是驻留在只连接一台路由器的网段上。由于只有一台路…

1000个已成功入职的软件测试工程师简历经验总结:软件测试工程师简历项目经验怎么写?(含真实简历模板)

一、前言&#xff1a;浅谈面试 面试是我们进入一个公司的门槛&#xff0c;通过了面试才能进入公司&#xff0c;你的面试结果和你的薪资是息息相关的。那如何才能顺利的通过面试&#xff0c;得到公司的认可呢?面试软件测试要注意哪些问题呢?下面和笔者一起来看看吧。这里分享一…

【面试常见】链表带环

前言 一、什么是环形链表 二、判断链表是否带环 三、&#xff08;问题1&#xff09;slow和fast一定会相遇吗&#xff1f; 四、&#xff08;问题2&#xff09;fast一次走3/4/n步&#xff0c;还会相遇吗&#xff1f; 五、总结 前言 链表是面试中常见的一类题。分为单链表&#x…

Maven详细教程(图文并茂)

一、maven概述 1.1、项目开发中的问题 1、我的项目依赖一些jar包&#xff0c;我把他们放在哪里&#xff1f;直接拷贝到项目的lib文件夹中?如果我开发的第二个项目还是需要上面的那些jar包&#xff0c;再把它们复制到我当前项目lib中&#xff1f;那如果现在是第三次了&#xf…

ALSA子系统(十八)------指纹解锁动画提示声卡顿问题解析

你好&#xff01;这里是风筝的博客&#xff0c; 欢迎和我一起交流。 很久没写kernel相关的东西了&#xff0c;主要是来到手机厂之后&#xff0c;大部分还是在Android上&#xff0c;Kernel虽然也有涉及&#xff0c;但毕竟只是有所涉及&#xff0c;主要业务逻辑还是在HAL之上&am…

【c语言】详解 结构体的内存对齐补齐

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

计算机网络的性能指标

1.计算机网络的性能指标 笔记来源&#xff1a;湖科大教书匠&#xff1a;计算机网络的性能指标 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 1.1 速率 注意&#xff1a;数据量中1KB 2 10 B 2^{10}B 210B、数据率中1kb/s 1 0 3 10^{3} 103b/s 1.2…

Chatgpt如何引入新的知识?我们来看下ACL2023 预训练模型能否对新注入的知识进行推理这篇文章

一、概述 title&#xff1a;Can LMs Learn New Entities from Descriptions? Challenges in Propagating Injected Knowledge 论文地址&#xff1a;https://arxiv.org/abs/2305.01651 相关代码&#xff1a; EKP数据和代码&#xff1a;GitHub - yasumasaonoe/entity_knowle…

香橙派4和树莓派4B构建K8S集群实践之四:BuildKit与LNMP

目录 1. 说明 2. 开始前的准备工作 2.1 docker 验证用户信息设置 2.2 安装BuildKit 3. 安装步骤 3.1 申请一个pvc存储区 (wwwroot-pvc.yaml) 3.2 Nginx 3.3 php-fpm 3.3.1 构建并推送镜像 4. 遇到的问题 5. 相关命令 6. 参考 1. 说明 k8s带来的灵活性&#xff0c;使…