OpenGL ES 纹理(7)

news2024/12/24 0:17:51

OpenGL ES 纹理(7)

简述

通过前面几章的学习,我们已经可以绘制渲染我们想要的逻辑图形了,但是如果我们想要渲染一张本地图片,这就需要纹理了。
纹理其实是一个可以用于采样的数据集,比较典型的就是图片了,我们知道我们的片段着色器会对每一个像素都执行一次来计算,该像素应该渲染什么颜色,纹理就是一个数据集,比如想要渲染一个图片,我们就是用图片的所有像素信息作为总数据集,然后片段着色器计算的时候就根据像素坐标去图片纹理数据集中取出对应的像素。
这一节我们就通过纹理来渲染一张图片。

接口介绍

  • glGenTextures 申请分配纹理
    public static native void glGenTextures(int n, int[] textures, int offset);
    第一个参数为需要分配纹理个数,第二个申请的纹理句柄(是一个出参),第三个是偏移
  • glTexParameteri 配置纹理参数
    public static native void glTexParameteri(int target, int pname, int param);
    第一个参数是目标,一般使用GL_TEXTURE_2D,第二和第三个参数分别是需要配置的参数的名称和值。
    下面我们列举几个常用的参数:
    • GL_TEXTURE_MAG_FILTER 当显示区域大于原来纹理时,如何放大图像
      • GL_NEAREST 会使用靠近的像素作为扩增的像素(计算量小但是效果差)
      • GL_LINEAR 求附近像素的加权平均值
    • GL_TEXTURE_MIN_FILTER 当显示区域小于原来纹理时,如何缩小图像,GL_NEAREST, GL_LINEAR值和GL_TEXTURE_MAG_FILTER类似。
    • GL_TEXTURE_WRAP_T / GL_TEXTURE_WRAP_S
      • 纹理的ST坐标系,对应XY坐标,纹理的坐标范围是0-1,这个参数就是控制如果超出这个坐标范围的表现。GL_CLAMP会使用边缘拉伸,GL_REPEAT会重复使用图像填充
  • GLUtils.texImage2D 加载纹理
    用于使用位图来填充纹理
  • glGenerateMipmap 开启多级细节。我们看越远的东西就会越小,这时候可能不需要加载完整的图像,可以降低图像细节提高性能。

纹理坐标系

我们之前提过,OpenGL ES的坐标系是(-1,-1)->(1,1)

在OpenGL ES中,纹理的坐标系是(0,0)-> (1,1)的,且纹理的起始坐标(0,0)是在左上角。

纹理渲染图片

原图

在这里插入图片描述

顶点数据

顶点数据和绘制正方形时候类似,依旧是4个顶点,然后索引缓冲区中6个点,以两个三角形拼接成一个正方形。
其中每个顶点有5个数据,前三个作为OpenGL的坐标,后两个作为纹理坐标,这里坐标对应关系是OpenGLES坐标系(-0.5,-0.5)对应纹理坐标系(1, 1),即OpenGL ES左下角对应图片的右下角,最终效果是图片顺时针旋转来90度。

private float[] vertexArray = new float[] {
        -0.5f, -0.5f, 0.0f, 1, 1f,
        0.5f, -0.5f, 0.0f, 1f, 0f,
        -0.5f, 0.5f, 0.0f, 0f, 1f,
        0.5f, 0.5f, 0.0f, 0f, 0f,
};

private short[] indexArray = new short[] {
        0,1,2,
        1,2,3
};

着色器

顶点着色器有两个属性,一个vPosition为OpenGL ES坐标,另一个vCoordinate为纹理坐标。纹理坐标会通过varying变量outCoordinate透传给片段着色器。
片段着色器中有一个统一变量vTexture,类型为sampler2D,它相当于纹理的句柄,我门通过texture2D方法,传入纹理句柄和当前纹理坐标即可获取当前坐标的颜色。

private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
                "attribute vec2 vCoordinate;" +
                "varying vec2 outCoordinate;" +
                "void main() {" +
                "  gl_Position = vPosition;" +
                "  outCoordinate = vCoordinate;" +
                "}";

private final String fragmentShaderCode =
        "precision mediump float;" +
                "varying vec2 outCoordinate;" +
                "uniform sampler2D vTexture;" +
                "void main() {" +
                "  gl_FragColor = texture2D(vTexture,outCoordinate);" +
                "}";

顶点数据填充并加载纹理

顶点填充数据逻辑和孩子i去哪都一样,这里额外调用来一个loadTexture(getContext(), R.drawable.test),R.drawable.test是我们的需要显示的图片资源,我们来看看loadTexture里面做了什么。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 清除颜色
    GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 创建顶点缓冲区
    int[] idBuffer = new int[2];
    GLES30.glGenBuffers(2, idBuffer, 0);
    vertexBufferId = idBuffer[0];
    elementBufferId = idBuffer[1];

    // 顶点缓冲区数据填充
    FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexBuffer.put(vertexArray);
    vertexBuffer.position(0);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER,
            vertexArray.length * 4,
            vertexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

    ShortBuffer indexBuffer = ByteBuffer.allocateDirect(indexArray.length * 4).order(ByteOrder.nativeOrder()).asShortBuffer();
    indexBuffer.put(indexArray);
    indexBuffer.position(0);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);
    GLES30.glBufferData(
            GLES30.GL_ELEMENT_ARRAY_BUFFER,
            indexArray.length * 4,
            indexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);

    loadTexture(getContext(), R.drawable.test);

    // shader
    shaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}
loadTexture

这里调用的方法我们在之前都已经介绍过了,这个过程其实和创建顶点缓冲区很想,先申请一个纹理,申请纹理会返回一个纹理句柄,然后绑定纹理,配置参数,配置纹理对应的位图,再解绑纹理,后续就可以通过这个纹理句柄来使用这个纹理了。

public int loadTexture(final Context context, final int resourceId) {
    GLES30.glGenTextures(1, textureHandle, 0);

    if (textureHandle[0] != 0) {
        // 绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);

        // 设置纹理参数
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);

        // 加载纹理
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId);
        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);

        // 生成多级细节
        GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);

        // 释放Bitmap资源
        bitmap.recycle();
    }
    // 解绑纹理
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);

    return textureHandle[0];
}

顶点布局以及渲染

渲染的流程和之前类似,处理配置顶点布局以外,这里主要新增的逻辑就是需要通过glBindTexture绑定纹理,还有将纹理句柄通过统一变量传给片段着色器。

public void onDrawFrame(GL10 gl) {
    // 清除屏幕
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    // 使能着色器程序
    GLES30.glUseProgram(shaderProgramId);
    // 绑定纹理
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");
    GLES30.glEnableVertexAttribArray(positionLocation);
    GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 5 * 4, 0);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    int coordinateLocation = GLES30.glGetAttribLocation(shaderProgramId, "vCoordinate");
    GLES30.glEnableVertexAttribArray(coordinateLocation);
    GLES30.glVertexAttribPointer(coordinateLocation, 2, GLES30.GL_FLOAT, false, 5 * 4, 3 * 4);

    // 配置纹理句柄到统一变量中去
    int vTextureLocation = GLES30.glGetUniformLocation(shaderProgramId, "vTexture");
    GLES30.glUniform1f(vTextureLocation, textureHandle[0]);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);
    // 调用DrawCall绘制三角形
    GLES30.glDrawElements(GLES30.GL_TRIANGLES, 6, GLES30.GL_UNSIGNED_SHORT, 0);

    // 清除配置
    GLES30.glDisableVertexAttribArray(positionLocation);
    GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
    GLES30.glUseProgram(0);
}

效果

如预期一样,这里相比原图顺时针旋转了90度。

在这里插入图片描述

小结

本节通过演示如何使用纹理加载渲染一个图片来介绍了纹理的使用方式,除此之外还通过纹理坐标系和OpenGL ES坐标系的映射关系将图片做了一个旋转,其实我们还有一个方式专门来做图像的变换,就是变化矩阵,我们下一节会专门来介绍这个。

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

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

相关文章

【ios】---swift开发从入门到放弃

swift开发从入门到放弃 环境swift入门变量与常量类型安全和类型推断print函数字符串整数双精度布尔运算符数组集合set字典区间元祖可选类型循环语句条件语句switch语句函数枚举类型闭包数组方法结构体 环境 1.在App Store下载Xcode 2.新建项目(可以先使用这个&…

AKShare-股票数据-相关股票

AKShare-股票数据-相关股票 数据科学实战 数据科学实战 2024年10月01日 13:53 作者寄语 本次更新股票数据-相关股票接口。主要修复该接口,目前通过该接口可以获取 时间,股票代码,相关股票代码,涨跌幅 等字段的数据。 欢迎加入专…

Java之方法的使用

修饰符 返回值 方法名称(形式参数){ } 当无参数的时候形式参数中什么都不写。 列如求两个数相加 修饰符可有可无。 方法重载: 1.方法名相同 2.参数列表不同 3。返回值不影响重载

STL--string类

我们从这篇文章之后就正式开始学习STL的string,字面看起来是不是像C语言里面的字符串之类的处理方法,是的,C里面也是对字符串的一些处理函数,但是C有很多这样的函数,给大家推荐一个网站 ,这个网站是C的官网…

Python | Leetcode Python题解之第448题找到所有数组中消失的数字

题目&#xff1a; 题解&#xff1a; class Solution:def findDisappearedNumbers(self, nums: List[int]) -> List[int]:n len(nums)for num in nums:x (num - 1) % nnums[x] nret [i 1 for i, num in enumerate(nums) if num < n]return ret

Verilog基础:$display系统函数和C语言中的库函数printf的区别

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 Verilog中的$display系统函数和C语言中的库函数printf都是用于输出信息&#xff0c;但它们的用法存在一定差别&#xff0c;本文将简要描述。 $display系统函数的B…

Javaweb商城项目

smbms 视频教程&#xff1a;javaweb-30&#xff1a;smbms项目搭建_哔哩哔哩_bilibili 一.项目分析 1.项目结构 2.数据库表 CREATE DATABASE smbms;USE smbms;DROP TABLE IF EXISTS smbms_address;CREATE TABLE smbms_address (id bigint(20) NOT NULL AUTO_INCREMENT CO…

计算机毕业设计 家校互联管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

2024年录屏软件排行榜:班迪录屏等四款软件实测!

不论是学习、娱乐还是工作&#xff0c;都有可能需要用到屏幕录制功能。在这篇文章中&#xff0c;我们将从不同的使用场景出发&#xff0c;为大家推荐几款实用的录屏工具——福昕录屏大师、转转大师录屏、爱拍录屏以及班迪录屏。 Foxit REC 直达链接&#xff08;复制到浏览器打…

Flexible组件的用法

文章目录 1. 概念介绍2. 使用方法3. 示例代码我们在上一章回中介绍了扩展内容相关的知识,本章回中将介绍Flexible组件.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在前面章回中介绍了扩展列表相关的内容,当页面中其它组件和扩展列表一起使用时,扩展列表有可能会…

SpringBoot——基础配置

但是还需要删除pom.xml中的标签——模板的文件也同样操作 banner的选项——关闭 控制台 日志 banner图片的位置——还会分辨颜色 在 Java 的日志框架&#xff08;如 Logback、Log4j2 等&#xff09;中&#xff0c;logging.level.root主要用于设置根日志记录器的日志级别…

【Redis】如何在 Ubuntu 上安装 Redis 5

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 本期内容主要介绍如何在 Ubuntu 上安装 Redis5 一些碎碎念&#xff1a; 本来这期内容介绍如何在 Centos 安装 Redis …

常用的Java安全框架

Spring Security&#xff1a; 就像Java安全领域的“瑞士军刀”&#xff0c;功能全面且强大。 支持认证、授权、加密、会话管理等安全功能。 与Spring框架无缝集成&#xff0c;使用起来特别方便。 社区活跃&#xff0c;文档丰富&#xff0c;遇到问题容易找到解决方案。 Apach…

SigmaStudio控件Cross Mixer\Signal Merger算法效果分析

衰减与叠加混音算法验证分析一 CH2:输入源为-20dB正弦波1khz CH1叠加混音&#xff1a;参考混音算法https://blog.csdn.net/weixin_48408892/article/details/129878036?spm1001.2014.3001.5502 Ch0衰减混音&#xff1a;外部多个输入源做混音时&#xff0c;建议参考该算法控件&…

网络通信——OSPF协议(基础篇)

这里基础是因为没有讲解OSPF中的具体算法过程&#xff0c;以及其中很多小细节。后续会更新。 目录 一.OSPF的基础信息 二.认识OSPF中的Router ID 三.OSPF中的三张表 四.OSPF中的度量方法&#xff08;计算开销值&#xff09; 五. OSPF选举DR和BDR&#xff08;就是这个区域…

简单vue指令实现 el-table 可拖拽表格功能

安装 SortableJS sorttableJs 相关优点如下&#xff1a; 相关配置项 参考 &#x1f449; SortableJS中文官网 pnpm i sortablejs封装成指令 不多逼逼&#xff0c;直接上才艺 &#x1f92a;&#x1f92a;&#x1f92a; 先安装一个 nanoid 插件 用于生成随机id&#xff0c;注…

图神经网络:处理复杂关系结构与图分类任务的强大工具

创作不易&#xff0c;您的打赏、关注、点赞、收藏和转发是我坚持下去的动力&#xff01; 图神经网络&#xff08;Graph Neural Network, GNN&#xff09;是针对图数据的一类神经网络模型。图数据具有节点&#xff08;节点代表实体&#xff09;和边&#xff08;边代表节点之间的…

LeetCode[中等] 55.跳跃游戏

给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 思路 贪心算法 可达位置…

html5 + css3(下)

目录 CSS基础体验cssCSS引入方式选择器选择器-标签选择器-类选择器-id选择器-通配符 文字基本样式1.1 字体大小1.2 字体粗细1.3 字体样式&#xff08;是否倾斜&#xff09; 文字-字体1.4 常见字体系列&#xff08;了解&#xff09;1.5 字体系列 拓展-层叠性font复合属性文本缩进…

erlang学习:Linux命令学习8

shell脚本案例学习 循环求 1-100 的每一步和 —案例 j0 i1 while((i<100)) do j$((ji)) echo $j ((i)) done每 30 s循环判断一次 user 用户是否登录系统 —案例 设置了一个次数&#xff0c;如果循环了五次在user文件中添加user用户&#xff0c;表示用户登录 USERS"u…