草庐IT

OpenGL-ES 学习(7)---- 纹理

特立独行的佩奇 2023-03-28 原文

纹理简介

现实生活中,纹理(Texture) 类似于游戏中皮肤的概念,最通常的作用是装饰3D物体,它像贴纸一样贴在物体的表面,丰富物体的表面和细节
在OpenGL-ES 开发中,纹理除了用于装饰物体表面,还可以用来作为存储数据的容器
所以在OpenGL-ES中,纹理实际上是一个可以被采样的复杂数据集合,是GPU的图像数据结构,纹理分为2D纹理、立方图纹理和3D纹理

  • 2D纹理是OpenGL-ES中最常见和最常用的纹理形式,是一个表示图像数据的二维数组,纹理中一个单独的数据单元被称为纹素或者纹理像素
  • 立方图纹理是一个由6个单独的2D纹理面组成的纹理,立方图纹理像素的读取是使用三维坐标(s, t, r)作为纹理坐标
  • 3D纹理可以看做2D纹理的集合,2D纹理是3D纹理的一个切面,使用三维坐标对齐进行访问

纹理映射

在OpenGL-ES中,纹理映射就是通过为图元的顶点坐标指定恰当的纹理坐标,通过纹理坐标在纹理图中选定特定范围的纹理区域,最后通过纹理坐标和顶点的映射关系,将选定的纹理区域映射到指定的图元上
纹理映射也称为纹理贴图,简单说就是将纹理坐标所指定的纹理区域,映射到顶点坐标对应的渲染区域
纹理坐标是使用纹理坐标系
顶点坐标是使用渲染坐标系或者OpenGL-ES坐标系

纹理坐标映射.jpg

4个纹理坐标T0(0,0), T1(0,1), T2(1,1), T3(1,0) 对应的顶点坐标为V0(-1,0.5),V1(-1,-0.5),V2(1,-0.5),V3(1,0.5)
OpenGL-ES 的基本图元是以三角形为单位的,设置绘制两个三角形V0V1V2和V0V2V3
当我们调整纹理坐标的顺序保持顶点坐标的顺序不变,T0T1T2T3 ==》T1T2T3T0绘制后将会得到一个顺时针旋转90度为纹理贴图,所以调整纹理坐标和顶点坐标的对应关系可以实现纹理贴图的简单旋转
纹理坐标和纹理像素的映射关系如下:
纹理坐标和纹理像素.png

纹理坐标(0,0)对应纹理像素的第一个元素
纹理坐标(1,0)对应纹理像素的index = width - 1的元素
纹理坐标(0,1)对应纹理像素的index = width* (height - 1) 的元素
纹理坐标(1,1)对应纹理像素的index = width* height - 1 的元素
对应纹理像素区域内的元素都会被采样

纹理映射流程

纹理映射的一般步骤:

  • 生成纹理,编译链接着色器程序
  • 确定纹理坐标和对应的顶点坐标
  • 加载图形数据到纹理,加载顶点坐标和纹理坐标到着色器程序
  • 开始绘制

软件流程如下:


texture_rendering.jpg

对纹理采样的FragmentShader:

        "#version 300 es                                     \n"
        "precision mediump float;                            \n"
        "in vec2 v_texCoord;                                 \n"
        "layout(location = 0) out vec4 outColor;             \n"
        "uniform sampler2D s_texture;                        \n"
        "void main()                                         \n"
        "{                                                   \n"
        "  outColor = texture( s_texture, v_texCoord );      \n"
        "}      

其中texture是内置的采样函数,v_texCoord 是顶点着色器传入的纹理坐标,根据纹理坐标进行采样,输出为4向量的 RGBA值
uniform sampler2D s_texture 是加载后的纹理内容

示例代码:
typedef struct
{
    // Handle to a program object
    GLuint programObject;
    // Sampler location
    GLint samplerLoc;
    // Texture handle
    GLuint textureId;
} UserData;

// load texture form tga file 
static GLuint CreateSimpleTexture2D()
{
    // Texture object handle
    GLuint textureId;

    // load texture from tga file
    const char* files = "C:/Users/86185/Documents/Visual Studio 2015/Projects/opengl_demo/opengl_demo/Huskey.tga";
    int width;
    int height;
    GLubyte* pixels = esLoadTGA(NULL, files, &width, &height);

    // Use tightly packed data
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Generate a texture object
    glGenTextures(1, &textureId);

    // Bind the texture object
    glBindTexture(GL_TEXTURE_2D, textureId);

    // Load the texture notice width height  format must align with real size
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);

    // Set the filtering mode
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    return textureId;

}

///
// Initialize the shader and program object
//
static int Init(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    char vShaderStr[] =
        "#version 300 es                            \n"
        "layout(location = 0) in vec4 a_position;   \n"
        "layout(location = 1) in vec2 a_texCoord;   \n"
        "out vec2 v_texCoord;                       \n"
        "void main()                                \n"
        "{                                          \n"
        "   gl_Position = a_position;               \n"
        "   v_texCoord = a_texCoord;                \n"
        "}                                          \n";

    char fShaderStr[] =
        "#version 300 es                                     \n"
        "precision mediump float;                            \n"
        "in vec2 v_texCoord;                                 \n"
        "layout(location = 0) out vec4 outColor;             \n"
        "uniform sampler2D s_texture;                        \n"
        "void main()                                         \n"
        "{                                                   \n"
//使用内建函数在Fragmanet Shader 中进行纹理采样
        "  outColor = texture( s_texture, v_texCoord );      \n"
        "}                                                   \n";

    // Load the shaders and get a linked program object
    userData->programObject = esLoadProgram(vShaderStr, fShaderStr);

    // Get the sampler location
    userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");

    // Load the texture
    userData->textureId = CreateSimpleTexture2D();

    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    return TRUE;
}

///
// Draw a triangle using the shader pair created in Init()
//
static void Draw(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLfloat vVertices[] = { -0.5f,  0.5f, 0.0f,  // Position 0
        0.0f,  0.0f,        // TexCoord 0 
        -0.5f, -0.5f, 0.0f,  // Position 1
        0.0f,  1.0f,        // TexCoord 1
        0.5f, -0.5f, 0.0f,  // Position 2
        1.0f,  1.0f,        // TexCoord 2
        0.5f,  0.5f, 0.0f,  // Position 3
        1.0f,  0.0f         // TexCoord 3
    };
    GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

    // Set the viewport
    glViewport(0, 0, esContext->width, esContext->height);

    // Clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // Use the program object
    glUseProgram(userData->programObject);

    // Load the vertex position
    glVertexAttribPointer(0, 3, GL_FLOAT,
        GL_FALSE, 5 * sizeof(GLfloat), vVertices);
    // Load the texture coordinate
    glVertexAttribPointer(1, 2, GL_FLOAT,
        GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    // Bind the texture
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, userData->textureId);

    // Set the sampler texture unit to 0
    glUniform1i(userData->samplerLoc, 0);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}

注意其中的glTexImage2D是用于加载纹理的函数:
void glTexImage2D( GLenum target,GLint level,GLint internalFormat,GLsizei width,GLsizei height,GLint border,
GLenum format,GLenum type,const void * data);

  • GLenum target : 指定texture 类型,一般为 GL_TEXTURE_2D
  • GLint level : 一般设置为0
  • GLint internalFormat : 设置纹理的存储格式 GL_RGB
  • GLsizei width : texture 的宽度
  • GLsizei height : texture 的高度
  • GLint border : 一般设置为0
  • GLenum format : 设置纹理输入图片的存储格式 GL_RGB
  • GLenum type : 设置纹理输入图片的存储格式 GL_RGB
  • const void * data : 指向加载为纹理内容的图片的指针

实际显示效果:


texture_result.jpg

纹理的环绕和过滤方式

纹理坐标的范围通常是从(0, 0)到(1, 1),如果设置的坐标超出这个范围,就会有重复的效果,比如 纹理的 S 坐标设置到 2,就表示 S 轴上要重复两次采样纹理,如果 将T 坐标设置为 2,表示 T 轴上重复两次采样纹理,通过设置不同的纹理坐标,就可以产生不同的重复的效果,纹理的环绕方式也可以设置,可以设置的类型如下:


纹理环绕方式.jpg
GL_REPEAT.jpg
GL_MIRRORED_REPEAT.jpg

GL_CLAMP_TO_EDGE.jpg

设置的代码如下:

// GL_REPEAT  GL_CLAMP_TO_EDGE  GL_MIRRORED_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
纹理的过滤方式

纹理映射的原理是:光栅化之后,图元转换为一个个光栅,每个光栅化之后的 pixel 都会经过 PixelShader,PixelShader 中从纹理采样得到每个 pixel 的颜色,这里就会涉及采样方式的问题,如果图元的分辨率很大,但是关联到图元的纹理很小,就需要进行插值,纹理的过滤方式实际就是纹理到光栅的插值算法
现在只讨论最重要的两种:GL_NEAREST和GL_LINEAR

  • GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素,原理是最近邻插值算法

  • GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的四个纹理像素的值,计算出一个插值,原理是双线性插值算法

邻近过滤算法简单,但是在放大图像的时候,会有严重的马赛克,线性过滤计算过程复杂,但是效果比邻近过滤算法好


GL_NEAREST.jpg

参考网址:
纹理映射

有关OpenGL-ES 学习(7)---- 纹理的更多相关文章

  1. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  8. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  9. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

  10. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

随机推荐