OpenGLES:单纹理贴图

news2024/11/18 21:37:23

一.概述

最近疏于写博客,接下来会陆续更新这段时间OpenGLES的一些开发过程。

前两篇OpenGLES的博客讲解了怎样使用OpenGLES实现相机普通预览多宫格滤镜

在相机实现过程中,虽然使用到了纹理,但只是在生成一个纹理之后,使用纹理去创建SurfaceTexture,然后再用SurfaceTexture创建Surface,并没有使用纹理进行图片渲染,也就是纹理贴图。

之前的博文《OpenGL:纹理》中使用OpenGL实现了正方形箱子的纹理贴图,而且也详细介绍了纹理的生成、纹理坐标、设置过滤、环绕方式等。

本篇博客要讲解是OpenGLES的纹理贴图,两者原理其实是一样的,但是本篇博客更偏向于实际应用。

以下主要是代码演示,跟随代码注释了解实现过程。

二.Render类:

public class ImgRender implements GLSurfaceView.Renderer {
    private final String TAG = ImgRender.class.getSimpleName();

    private final Context mContext;

    private float vertexData[] = {
            -1f, -1f,  //左下
            1f, -1f,   //右下
            -1f, 1f,   //左上
            1f, 1f,    //右上
    };

    //Android 纹理原点在左上角
    private float textureData[] = {
            0.0f, 1.0f,  //左上
            1.0f, 1.0f,  //右上
            0.0f, 0.0f,  //左下
            1.0f, 0.0f,  //右下
    };

    //shader程序/渲染器
    private int shaderProgram;
    //纹理id
    private int[] textureId = new int[1];
    //顶点坐标
    private int aPosition;
    //纹理坐标
    private int aTexCoord;
    //采样器
    private int sampler;
    //顶点数据缓存
    private FloatBuffer vertexBuffer;
    //纹理数据缓存
    private FloatBuffer textureBuffer;
    //一个顶点有几个数据
    private int VERTEX_POSITION_SIZE = 2;
    //一个纹理点有几个数据
    private int TEXTURE_POSITION_SIZE = 2;

    public ImgRender(Context context) {
        mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.v(TAG, "onSurfaceCreated()");
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        initGLES();
    }

    public void initGLES() {
        Log.v(TAG, "initGLES!");

        /************** 着色器程序/渲染器 **************/
        //创建并连接 着色器程序
        shaderProgram = ShaderUtils.createAndLinkProgram(mContext,
                "img_vertex_shader.glsl",
                "img_fragtment_shader.glsl");
        if (shaderProgram == 0) {
            Log.v(TAG, "create And Link ShaderProgram Fail!");
            return;
        }
        //使用着色器源程序
        glUseProgram(shaderProgram);

        /************** 着色器变量 **************/
        //从着色器程序中获取各个变量
        aPosition = glGetAttribLocation(shaderProgram, "aPosition");
        aTexCoord = glGetAttribLocation(shaderProgram, "aTexCoord");
        sampler = glGetUniformLocation(shaderProgram, "sampler");
        //将片段着色器的采样器(纹理属性:sampler)设置为0号单元
        glUniform1i(sampler, 0);

        vertexBuffer = ShaderUtils.getFloatBuffer(vertexData);
        textureBuffer = ShaderUtils.getFloatBuffer(textureData);

        //创建纹理对象
        glGenTextures(textureId.length, textureId, 0);

        TextureUtils.LoadTexture(mContext, textureId[0], R.drawable.bg2);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.v(TAG, "onSurfaceChanged(): " + width + " x " + height);

        glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //Log.v(TAG, "onDrawFrame()");
        glClear(GL_COLOR_BUFFER_BIT);

        /********* 顶点操作 **********/
        glEnableVertexAttribArray(aPosition);
        glEnableVertexAttribArray(aTexCoord);

        glVertexAttribPointer(aPosition, VERTEX_POSITION_SIZE, GL_FLOAT, false, 2 * 4, vertexBuffer);
        glVertexAttribPointer(aTexCoord, TEXTURE_POSITION_SIZE, GL_FLOAT, false, 2 * 4, textureBuffer);

        /********* 纹理操作:激活、绑定 **********/
        glActiveTexture(GL_TEXTURE);
        glBindTexture(GL_TEXTURE_2D, textureId[0]);

        /********* 绘制 **********/
        //绘制vertexData.length/2即4个点
        glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexData.length / 2);
        /********* 纹理操作:解除绑定 **********/
        glBindTexture(GL_TEXTURE_2D, 0);

        //关闭顶点数组的句柄
        glDisableVertexAttribArray(aPosition);
        glDisableVertexAttribArray(aTexCoord);
    }
}

三.ShaderUtils相关函数:

3.1 createAndLinkProgram()

    /*
     * 创建和链接着色器程序
     * 参数:顶点着色器、片段着色器程序ResId
     * 返回:成功创建、链接了顶点和片段着色器的着色器程序Id
     */
    public static int createAndLinkProgram(Context context, String vertexShaderFN, String fragShaderFN) {
        //创建着色器程序
        int shaderProgram = glCreateProgram();
        if (shaderProgram == 0) {
            Log.e(TAG, "Failed to create shaderProgram ");
            return 0;
        }

        //获取顶点着色器对象
        int vertexShader = loadShader(GL_VERTEX_SHADER, loadShaderSource(context, vertexShaderFN));
        if (0 == vertexShader) {
            Log.e(TAG, "Failed to load vertexShader");
            return 0;
        }

        //获取片段着色器对象
        int fragmentShader = loadShader(GL_FRAGMENT_SHADER, loadShaderSource(context, fragShaderFN));
        if (0 == fragmentShader) {
            Log.e(TAG, "Failed to load fragmentShader");
            return 0;
        }

        //绑定顶点着色器到着色器程序
        glAttachShader(shaderProgram, vertexShader);
        //绑定片段着色器到着色器程序
        glAttachShader(shaderProgram, fragmentShader);

        //链接着色器程序
        glLinkProgram(shaderProgram);
        //检查着色器链接状态
        int[] linked = new int[1];
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            glDeleteProgram(shaderProgram);
            Log.e(TAG, "Failed to link shaderProgram");
            return 0;
        }

        return shaderProgram;
    }

3.2 getFloatBuffer()

    public static FloatBuffer getFloatBuffer(float[] array) {
        //将顶点数据拷贝映射到 native 内存中,以便opengl能够访问
        FloatBuffer buffer = ByteBuffer
                .allocateDirect(array.length * BYTES_PER_FLOAT)//直接分配 native 内存,不会被gc
                .order(ByteOrder.nativeOrder())//和本地平台保持一致的字节序(大/小头)
                .asFloatBuffer();//将底层字节映射到FloatBuffer实例,方便使用

        buffer.put(array)//将顶点拷贝到 native 内存中
                .position(0);//每次 put position 都会 + 1,需要在绘制前重置为0

        return buffer;
    }

四.TextureUtils相关函数

4.1 LoadTexture()

    //纹理Id由外部传入
    public static void LoadTexture(Context context, int textureId, int bitmapResId) {
        //绑定纹理:将纹理放到当前单元的 GL_TEXTURE_BINDING_EXTERNAL_OES 目标对象中
        glBindTexture(GL_TEXTURE_2D, textureId);
        //配置纹理:过滤方式
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        /************* bitmap **************/
        //获取图片的 bitmap
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId);
        //绑定 bitmap 到textureIds[1]纹理
        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();//用完及时回收

        //解绑纹理 指的是离开对 纹理的配置,进入下一个状态
        glBindTexture(GL_TEXTURE_2D, 0);
    }

五.着色器代码

5.1 img_vertex_shader.glsl

#version 300 es

layout (location = 0) in vec4 aPosition;         //把顶点坐标给这个变量, 确定要画画的形状
layout (location = 1) in vec4 aTexCoord;         //接收纹理坐标,接收采样器采样图片的坐标

//传给片元着色器 像素点
out vec2 vTexCoord;

void main()
{
    //内置变量 gl_Position ,我们把顶点数据赋值给这个变量 opengl就知道它要画什么形状了
    gl_Position = aPosition;
    vTexCoord = aTexCoord.xy;
}

5.2 img_fragtment_shader.glsl

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;

in vec2 vTexCoord; //纹理坐标,图片当中的坐标点

uniform sampler2D sampler;  //图片,采样器

out vec4 outColor;

void main(){
    outColor = texture(sampler, vTexCoord);
}

六.Render、GLSurfaceView等实现过程

GLRender及其在GLSurfaceView中的设置:

mGLSurfaceView = rootView.findViewById(R.id.gl_SurfaceView);
//设置GLES版本
mGLSurfaceView.setEGLContextClientVersion(3);
//创建Render对象,并将其设置到GLSurfaceView
mImgRender = new ImgRender(getActivity());
mGLSurfaceView.setRenderer(mImgRender);
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);

还有GLSurfeaceView在Activity或Fragment中的加载,这些常规操作的代码就不再详细演示了。根据自己实际开发过程实现就行。

七.注意点

1.片段着色器中采样器的使用

有一点需要注意的是,在之前实现相机预览的片段着色器代码中,使用的采样器是:

uniform samplerExternalOES sCameraTexture;

现在渲染图片时使用的采样器是:

uniform sampler2D sampler; 

在实现OpenGLES纹理贴图过程中,最初直接延用了实现相机预览时的采样器samplerExternalOES,但是图片始终无法渲染成功。

这是因为"采样器:samplerExternalOES"是OpenGLES中专门用来采样YUV数据的,所以在实现相机预览时要使用它,但是渲染图片时,就要用常规的"采样器Sampler2D"了。

八.最终效果

使用了一张宇宙星空图,请忽略"打开应用,点击按钮"过程

最后那张宇宙星空图的呈现,就是本次OpenGLES博文所要实现的纹理贴图的最终效果:

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

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

相关文章

基于SpringBoot+Vue+支付宝支付的汽车租赁系统(可做毕设/课设)

技术栈 前后端分离 前端使用: Vue Element 后端使用: SpringBoot Mysql8.0 Mybatis 支付宝支付 功能 分为 管理员端 和 普通用户端 和 维修人员端 普通用户端 1.首页 展示所有品牌,汽车,公告,按关键字搜索汽车名 2.汽车详情页 展示汽车详情和评价 3.下单支付和退押金 立即下单…

vue3 - 开发和生产环境通过Mock模拟真实接口请求

GitHub Demo 地址 在线预览 在前端开发中,常常需要与后端接口进行交互。然而,在接口尚未实现或者正在开发的情况下,前端开发人员往往无法得到真实的接口数据,这给开发和测试工作带来了一定的困扰。对此,可以通过Mock模…

《动手学深度学习 Pytorch版》 7.7 稠密连接网络

7.7.1 从 ResNet 到 DenseNet DenseNet 可以视为 ResNet 的逻辑扩展。 ResNet 将函数展开为 f ( x ) x g ( x ) f(\boldsymbol{x})xg(\boldsymbol{x}) f(x)xg(x),即一个简单的线性项和一个复杂的非线性项。 若将 f f f 拓展成超过两部分,则 Dense…

CRM客户管理系统英文专业版

外资公司日常沟通的语言以英文为主,业务往来也是涉及到国内外,专业的英文版CRM系统很适合这样的业务团队,尤其CRM供应商是国际化企业,在海外也有分公司、办事处。 多语言 ZOHO支持多语种如英语、汉语、日语等28种语言&#xff0…

Next.js 13.5 正式发布,速度大幅提升!

9 月 19 日,Next.js 13.5 正式发布,该版本通过以下方式提高了本地开发性能和可靠性: 本地服务器启动速度提高 22%:使用App和Pages Router可以更快地进行迭代 HMR(快速刷新)速度提高 29%:在保存…

C++之指向引用的指针和指向指针的引用总结(二百三十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

【计算机视觉】图像的获取和表示——图像传感器技术|主要参数解析、成像原理剖析、传感器处理

博主简介:努力学习的22级计算机科学与技术本科生🌸博主主页: 是瑶瑶子啦每日一言🌼: 每一个不曾起舞的日子,都是对生命的辜负。——尼采 前言 文章目录 前言一、图像传感器技术1.0:前言1.1:两种…

自定义类型详解(上)

结构体 1 结构体的声明 1.1 结构的基础知识 结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag//struct是结构体的标志,tag是标签;名字。 {member-list;//成员变量 }variable-list;//变量列…

Nginx 关闭/屏蔽 PUT、DELETE、OPTIONS 请求

1、修改 nginx 配置 在 nginx 配置文件中,增加如下配置内容: if ($request_method !~* GET|POST|HEAD) {return 403; }修改效果如下: 2、重启 nginx 服务 systemctl restart nginx或者 service nginx restart3、功能验证 使用如下方式…

【计算机网络】互联网公司的网络架构和业务场景

互联网公司的网络架构和业务场景 1. 互联网公司网络的组成1.1 网络的物理组成1.2 骨干网组成1.3 数据中心网络组成 2.互联网公司网络服务场景2.1 通用服务场景2.1.1 客户端到服务端请求真实网络过程2.1.2 客户端到服务端请求抽象网络过程2.1.3 负载均衡网络模型 2.2 边缘服务场…

Xilinx FPGA 程序固化重新上电程序不运行的问题

问题描述 FPGA直接下载bit文件,功能正常。 FPGA擦除FLASH,烧写FLASH,正常。 电源断电,重新上电,FALSH里面的程序没有启动,FPGA程序没有跑起来。–FLASH启动不正常。 解决办法 在XDC约束文件里边增加约束: ## Configuration options, can be used for all designs se…

京东获得JD商品详情 API 返回值说明

京东商品详情API接口可以获得JD商品详情原数据。 这个API接口有两种参数,公共参数和请求参数。 公共参数有以下几个: apikey:这是您自己的API密钥,可以在京东开发者中心获取。 请求参数有以下几个: num_iid&#…

11 FPGA_简易电压表设计与验证(附代码)

1. 模数转换理论 模数转换器又称(A/D转换器),通常是指一个将模拟信号转变为数字信号的电子元件或电路。常见的转换方式使将模拟量与基准量比对得到便于传输的二进制信号。生活中常见的模拟量有温湿度、图像、声音等。模拟信号与数字信号的转换…

Spring学习笔记4 Bean的作用域

Spring学习笔记3 Spring对IOC的实现_biubiubiu0706的博客-CSDN博客 新建模块 spring-004 引入依赖 <dependencies><!--Spring依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId>&…

企业申报“专精特新”,对知识产权有哪些要求?

深科信从深圳市中小企业服务局发布的公开数据中了解到&#xff0c;2022年深圳市专精特新中小企业申报8000余家&#xff0c;通过4800余家。2023年深圳市专精特新企业认定火热申报中&#xff0c;10月15日截止。 成为“专精特新”企业有什么好处? 1.国家级资质强力背书&#xff…

WordPress主题DUX v8.2源码下载

新增产品分类左侧多级分类折叠显示 新增网站默认字体对 MiSans 和 HarmonyOS Sans 的支持 新增顶部左上角显示登录注册的模块开关&#xff0c;且支持原生登录方式 新增手机端导航菜单的关闭按钮 新增文章内容中标题二的强化展示 新增全站禁止复制、右键和选择的操作 新增文章内…

fastjson反序列化漏洞(CVE-2017-18349)

文章目录 fastjson序列化FastJson 序列化操作反序列化漏洞原理漏洞复现&#xff08;CVE-2017-18349&#xff09; fastjson fastjson 是阿里巴巴开发的 java语言编写的高性能 JSON 库,用于将数据在 Json 和 Java Object之间相互转换。它没有用java的序列化机制,而是自定义了一套…

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLA…

Quartus出租车计费器verilog计价器

名称&#xff1a;出租车计费器verilog计价器 软件&#xff1a;Quartus 语言&#xff1a;Verilog 要求&#xff1a; 出租车计费器&#xff0c;起步价10元&#xff0c;3公里内起步价&#xff0c;可以切换白天和夜晚计费&#xff0c;白天时超过3公里后2.4元每公里&#xff0c;停…

RabbitMQ实现秒杀场景示例

本文章通过MQ队列来实现秒杀场景 整体的设计如下图&#xff0c;整个流程中对于发送发MQ失败和发送到死信队列的数据未做后续处理 1、首先先创建MQ的配置文件 Configuration public class RabbitConfig {public static final String DEAD_LETTER_EXCHANGE "deadLetterE…