OpenGLES:多纹理贴图,文字水印

news2025/1/11 14:54:41

一.概述

上一篇博客讲解了OpenGLES怎么实现单纹理贴图

仅仅只绘制一张图片是不过瘾的

本篇博客讲解如何通过多纹理贴图实现图片和文本水印效果

在单纹理贴图基础上,多纹理贴图的区别主要有两点:

  • 纹理的生成、绑定等由单个变成多个
  • 文本内容先转换为Bitmap后再进行纹理贴图

知道这两点之后,实现起来就容易了

这篇博客我会使用多纹理实现一张背景大图,一张水印小图和一串文字水印的效果。

最终效果在最后会展示。

废话不多说,正篇开始!

二.Render类代码

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

    private final Context mContext;

    //背景图片顶点
    private float vertexData[] = {
            -1f, -1f,  //左下
            1f, -1f,   //右下
            -1f, 1f,   //左上
            1f, 1f,    //右上
    };

    //图片水印顶点
    private float vertexData1[] = {
            -1f, 0.5f,  //左下
            0f, 0.5f,   //右下
            -1f, 1f,    //左上
            0f, 1f,     //右上
    };

    //文本水印顶点
    private float vertexData2[] = {
            0f, -0.85f,   //左下
            1f, -0.85f,   //右下
            0f, -0.75f,  //左上
            1f, -0.75f    //右上
    };

    //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[3];
    //顶点坐标
    private int aPosition;
    //纹理坐标
    private int aTexCoord;
    //采样器
    private int sampler;
    //顶点数据缓存
    private FloatBuffer vertexBuffer;
    private FloatBuffer vertexBuffer1;
    private FloatBuffer vertexBuffer2;

    //纹理数据缓存
    private FloatBuffer textureBuffer;
    //一个顶点有几个数据
    private int VERTEX_POSITION_SIZE = 2;
    //一个纹理点有几个数据
    private int TEXTURE_POSITION_SIZE = 2;

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

    public void initData(Size size) {
    }

    @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);
        vertexBuffer1 = ShaderUtils.getFloatBuffer(vertexData1);
        vertexBuffer2 = ShaderUtils.getFloatBuffer(vertexData2);
        textureBuffer = ShaderUtils.getFloatBuffer(textureData);

        //设置文字支持透明
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        /************* 纹理 **************/
        //创建纹理对象1
        glGenTextures(textureId.length, textureId, 0);
        //激活纹理0:默认0号纹理单元,一般最多能绑16个,视GPU而定
        glActiveTexture(GL_TEXTURE);

        TextureUtils.LoadTexture(mContext,textureId[0], R.drawable.bg4);
        TextureUtils.LoadTexture(mContext,textureId[1], R.drawable.watermark);
        glBindTextureLoadTxt(textureId[2], "OpenGLES: Shawn.Xiao!");
    }

    @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);

        drawBg();
        drawWaterMarkImg();
        drawWaterMarkTxt();
    }

    private void drawBg() {
        /********* 顶点操作 **********/
        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);
    }

    private void drawWaterMarkImg() {
        /********* 顶点操作 **********/
        glEnableVertexAttribArray(aPosition);
        glEnableVertexAttribArray(aTexCoord);

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

        /********* 纹理操作:激活、绑定 **********/
        //glActiveTexture(GL_TEXTURE1);   //花了129元
        glBindTexture(GL_TEXTURE_2D, textureId[1]);
        /********* 绘制 **********/
        //绘制vertexData.length/2即4个点
        glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexData1.length / 2);
        /********* 纹理操作:解除绑定 **********/
        glBindTexture(GL_TEXTURE_2D, 0);

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

    private void drawWaterMarkTxt() {
        /********* 顶点操作 **********/
        glEnableVertexAttribArray(aPosition);
        glEnableVertexAttribArray(aTexCoord);

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

        /********* 纹理操作:激活、绑定 **********/
        glActiveTexture(GL_TEXTURE);
        glBindTexture(GL_TEXTURE_2D, textureId[2]);
        /********* 绘制 **********/
        //绘制vertexData.length/2即4个点
        glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexData.length / 2);
        /********* 纹理操作:解除绑定 **********/
        glBindTexture(GL_TEXTURE_2D, 0);

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

    private void glBindTextureLoadTxt(int textureId, String txt) {
        //绑定纹理:将纹理放到当前单元的 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 **************/
        //定义变量
        int textSize = 36;
        String textColor = "#ffff00";
        String bgColor = "#00000000";
        int padding = 0;
        //txt转换成bitmap
        Bitmap bitmap = BitmapUtils.createTextImage(txt, textSize, textColor, bgColor, padding);
        //绑定 bitmap 到textureIds[1]纹理
        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();//用完及时回收

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

三.ShaderUtils相关代码:

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);
    }

五.BitmapUtils相关代码

文字转换成Bitmap

    /**
     * 设置文字水印
     *
     * @param text      文本内容
     * @param textSize  文字大小
     * @param textColor 文字颜色
     * @param bgColor   文字背景颜色 #00000000
     * @param padding   文字与边距距离
     * @return 文字水印的 bitmap
     */
    public static Bitmap createTextImage(String text, int textSize, String textColor, String bgColor, int padding) {

        Paint paint = new Paint();
        paint.setColor(Color.parseColor(textColor));
        paint.setTextSize(textSize);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);

        float width = paint.measureText(text, 0, text.length());
        float top = paint.getFontMetrics().top;
        float bottom = paint.getFontMetrics().bottom;

        Bitmap bm = Bitmap.createBitmap((int) (width + padding * 2), (int) ((bottom - top) + padding * 2), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bm);
        canvas.drawColor(Color.parseColor(bgColor));
        canvas.drawText(text, padding, -top + padding, paint);
        return bm;
    }

六.着色器代码

着色器代码与上一篇博文中的单纹理贴图是一样的:

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;
}

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);
}

七.UI相关代码

Render、GLSurfaceView与Activity、Fragment等之间的实现逻辑请根据自己项目的实际情况去实现,这里只贴出Render在GLSurfaceView中设置的代码:

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

八.最终效果:

一张背景大图,一张水印小图,和一串水印文字:

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

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

相关文章

深度学习实战基础案例——卷积神经网络(CNN)基于Xception的猫狗识别|第2例

文章目录 一、环境准备二、数据预处理三、构建模型四、实例化模型五、训练模型5.1 构建训练函数5.2 构建测试函数5.3 开始正式训练 六、可视化精度和损失七、个体预测 我的环境: pytorch:2.0python:3.8jupyternotebook 一、环境准备 impor…

jmeter——接口压测和性能监测实践

一、安装JMeter 1. 在客户端机器上安装JMeter压测工具,我这里安装的版本是apache-jmeter-5.2.1,由于JMeter是JAVA语言开发的,所以安装JMeter压测工具前先安装JDK,一般安装JDK1.8及以上即可。安装完成后,如果客户端机器…

【广州华锐互动】利用VR开展工业事故应急救援演练,确保救援行动的可靠性和有效性

在工业生产中,事故的突发性与不可预测性常常带来巨大的损失。传统的应急演练方式往往存在场地限制、成本高、效果难以衡量等问题。然而,随着虚拟现实(VR)技术的快速发展,VR工业事故应急救援演练应运而生,为…

基于PLE结合卡尔曼滤波的RSSI定位算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ............................................................... for Num_xb Num_xb2Num_…

Elasticsearch 部署学习

文章目录 Elasticsearch 部署学习1. 单节点部署 elasticsearch1.1 部署 jdk1.2 下载 elasticsearch1.3 上传文件并修改配置文件1.4 启动1.5 问题总结1.6 浏览器验证 2. 集群部署 elasticsearch3. 常用命令4. Elasticsearch kibana安装:one: 参考部署文档:two: 下载对应版本的安…

75、SpringBoot 整合 MyBatis------使用 Mapper 作为 Dao 组件

总结: 添加一个User类和Mapper接口, 在Mapper接口这个类上面添加Mapper注解,就可以和数据库进行映射了, 然后在mapper接口写方法和sql, 在测试类进行测试。 pom文件就添加个mybatis-spring-boot-starter 的组件&#x…

C 语言简单入门

C 语言发展历史|标准 1972年,丹尼斯里奇(Dennis Ritch)和肯汤普逊(Ken Tompson)在贝尔实验室开发 UNIX 操作系统时基于 B 语言设计出 C 语言。 1987年,布莱恩柯林汉(Brian Kernighan&#xff…

Java核心知识点整理大全5-笔记

书接上回Java核心知识点整理大全4-笔记_希斯奎的博客-CSDN博客 目录 3.4.1. HashMap(数组链表红黑树) 3.4.1.1. JAVA7 实现 3.4.1.2. JAVA8 实现 3.4.2. ConcurrentHashMap 3.4.2.1. Segment 段 3.4.2.2. 线程安全(Segment 继承 ReentrantLo…

【C语言】联合体与结构体如何巧妙配合使用节省内存空间?

本篇文章目录 1. 联合体的特点2. 计算联合体占用内存大小3. 利用联合体的特点判断当前机器是以什么字节序顺序存储数据?4. 联合体什么时候使用? 1. 联合体的特点 联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员&#x…

微软最热门的10款前端开源项目!

本文来盘点微软开源的十大前端项目,这些项目在 Github 上获得了超过 45 万 Star! Visual Studio Code Visual Studio Code 是一款由微软开发的开源的代码编辑器。它支持多种编程语言,如C、C、C#、Python、JavaScript 和 TypeScript 等&…

Nginx 代理 MySQL 连接

文章目录 Nginx 代理 MySQL 连接1. 前言2. 部署 Nginx,MySQL3. ngx_stream_core_module 配置方式3.1 stream3.2 server3.3 listen3.4 配置示例 4. 限制访问 IP4.1 allow4.2 deny4.3 配置示例 5. 综合案例 Nginx 代理 MySQL 连接 原文地址:https://mp.wei…

Windows批处理文件 @echo off作用

bat批处理文件代码中有echo off 这样的语句,echo off是什么意思?在bat中扮演着什么作用呢? A: echo off的意思是在批处理运行命令的时候不会一条一条的显示执行的命令,与之相匹配的还有echo on。 echo off 与echo on …

机器视觉康耐视Visionpro-脚本编写标记标识:点,直线,矩形,圆

显示标记标识的重要作用就是,对NG或者OK对操作机器视觉的人去看到具体位置缺陷或者NG坐标。 一.点CogPointMarker CogPointMarker PointMarker1 = new CogPointMarker();//创建对象,点CogPointMarker //注意运行工具 PointMarker1.X = 100; PointMarker1

基于FPGA的16QAM调制verilog代码

名称:FPGA的16QAM调制verilog 软件:Quartus 语言:Verilog 要求: 使用FPGA实现16QAM的调制,并进行仿真 代码下载:FPGA的16QAM调制verilog_Verilog/VHDL资源下载 代码网:hdlcode.com 部分代…

万界星空科技MES与WMS如何集成的?

传统制造业数字化转型正汹涌而来,要进一步提高产业发展质量,重塑制造业竞争优势,就必须加快发展数字化制造,加紧推动制造业的数字化转型。在这一数字化背景下,新一代科技技术的运用尤为重要。在具体实践中,…

工具及方法 - 二进制编辑软件

之前介绍过用Notepad和VSCode进行二进制文件编辑。 很多通用型的文本编辑器都会集成二进制文件编辑功能,或者使用插件等形式扩展此项功能。比如vi/vim等工具。 而且,作为文本编辑、二进制文件编辑一类的工具,数量众多,各有特色。…

msvcp140为什么会丢失?msvcp140.dll丢失的解决方法

msvcp140.dll 是一个动态链接库文件,它包含了 C 运行时库的一些函数和类,例如全局对象、异常处理、内存管理、文件操作等。它是 Visual Studio 2015 及以上版本中的一部分,用于支持 C 应用程序的运行。如果 msvcp140.dll 丢失或损坏&#xff…

redis安装问题

title: “Redis安装问题” createTime: 2022-01-04T20:47:0608:00 updateTime: 2022-01-04T20:47:0608:00 draft: false author: “name” tags: [“redis”] categories: [“install”] description: “测试的” title: redis安装可能遇到的错误 createTime: 2022-01-04T20:47…

如何在pycharm专业版使用conda虚拟环境

目 录 本文背景 前提条件 操作步骤 1.查看当前虚拟环境 2.创建一个新的虚拟环境 3.查看虚拟环境 4.切换虚拟环境 5.无依赖运行pandas代码 6.终端安装依赖 7.再次运行 本文背景 在经历了之前痛苦的环境各种报错的情况下,我终于知道如何有序地管理环境了 那…

SPA项目之登录注册--请求问题(POSTGET)以及跨域问题

🥳🥳Welcome Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于Vue的相关操作吧 目录 🥳🥳Welcome Huihuis Code World ! !🥳🥳 一.ElementUI是什么 💡准备工作&…