• OpenGLES系列demo之(二):纹理映射


            本文主要会先阐述什么是纹理映射,并在前面文章的框架-- OpenGLES系列demo之框架简介 (后文简称“框架文章”)中上,编写一个简单的demo。

    一、纹理

            什么是纹理?纹理可以理解为物体表面的图案,它就像是贴纸一样贴在物体表面,丰富物体的表面和细节。在openGL中,纹理实际上是一个可以被采样的复杂数据集合,是 GPU 使用的图像数据结构。

            如下可以理解为,从左图到右图,物体表面上“贴”了一层墙纸,这层墙纸可以理解为纹理。

            

            纹理分为1D(一维),2D(二维),3D(三维),CUBE(立方体)等等。

            2D 纹理是 OpenGLES 中最常用和最常见的纹理形式,是一个图像数据的二维数组。纹理中的一个单独数据元素称为纹素或纹理像素。

            3D 纹理可以看作 2D 纹理作为切面的一个数组,类似于立方图纹理,使用三维坐标对其进行访问。

            立方图纹理是一个由 6 个单独的纹理面组成的纹理。立方图纹理像素的读取通过使用一个三维坐标(s,t,r)作为纹理坐标。

    二、纹理映射

            纹理映射通俗来讲,就是类似上面所说将“墙纸”贴到物体表面的过程。

            具体定义是通过为图元的顶点坐标指定恰当的纹理坐标,通过纹理坐标在纹理图中选定特定的纹理区域,最后通过纹理坐标与顶点的映射关系,将选定的纹理区域映射到指定图元上。

            这里面就涉及坐标系,图片用纹理坐标系来描述,渲染或者openGL显示用的是顶点坐标系。

            纹理坐标定义在单位化的正方形内,并且以左下角为原点(0,0),如下:

            不过值得注意的是,在Android平台上,我们一般用bitmap加载绑定纹理,而bitmap绑定的2D纹理是上下颠倒的。

            因此,如果是在Android平台上进行开发,且使用bitmap的话,我们在写坐标映射的时候,可以将左上角来作为原点,这样就不需要将图片颠倒,再以左下角为原点再颠倒的过程。负负得正,我们用左上角来当做原点,比较直观。如下要加载这张图片的话,纹理坐标可以为:

            4个纹理坐标分别为:

            T0(0,0),T1(0,1),T2(1,1),T3(1,0)。 

            对应的渲染坐标系/openGLES坐标系为:

            4个纹理坐标对应的顶点坐标分别为:

            V0(-1,0.5),V1(-1,-0.5),V2(1,-0.5),V3(1,0.5)。 

            openGLES绘制是以三角形为单位的,因此可以按照这样的顺序绘制2个三角形V0V1V2和V0V2V3,就可以呈现出来一张图片。

    三、代码实现纹理映射

    1、纹理映射的一般步骤为:

            (1)、加载图像数据到纹理;

            (2)、生成纹理,编译链接着色器程序;

            (3)、确定纹理坐标及对应的顶点坐标;

            (4)、加载纹理坐标和顶点坐标到着色器程序;

            (5)、绘制

    2、创建OpenGLES环境

            这个创建在之前的框架文章中搭建的框架基本上已经实现了openGLES环境,这里我们再简单过一下。

            (1)、简单自定义GLSurfackView。-- 参考“框架文章”中的MyGLSurfaceView;

            (2)、自定义Render实现GLSurfaceView.Renderer接口。 -- 参考“框架文章”中的MyGLRender;

            以上两步基本就创建好了openGLES环境,

            (3)、由于我们在c++层完成绘制,因此还需要与jni的一些接口调用。 -- 参考“框架文章”中的MyNativeRender等;

    3、编译链接着色器程序,实现绘制类

            以下部分我们是在c++层去操作,实现绘制。

    I、编译和链接着色器程序的类

            在我们的“框架文章”中,已经讲到将编译和绘制着色器相关的东西,提出到一个工具类GLUtils,这个类是通用的,实现编译和链接着色器程序等功能。这里不再赘述,看参考“框架”文章。

    II、纹理映射的类

            在这个纹理映射的绘制实现类TextureMapSample中,我们会实现纹理映射的绘制,显示一张图片。

            顶点着色器脚本:

    1. #version 300 es
    2. layout(location = 0) in vec4 a_position;
    3. layout(location = 1) in vec2 a_texCoord;
    4. out vec2 v_texCoord;
    5. void main()
    6. {
    7. gl_Position = a_position;
    8. v_texCoord = a_texCoord;
    9. }

            对纹理采样的片元着色器脚本:

    1. #version 300 es
    2. precision mediump float;
    3. in vec2 v_texCoord;
    4. layout(location = 0) out vec4 outColor;
    5. uniform sampler2D s_TextureMap; //声明采用器
    6. void main()
    7. {
    8. // texture() 为内置的采样函数,v_texCoord 为顶点着色器传进来的纹理坐标
    9. // 根据纹理坐标对纹理进行采样,输出采样的 rgba 值(4维向量)
    10. outColor = texture(s_TextureMap, v_texCoord);
    11. }

            上面两个脚本在init函数中可以先加载,并在init函数中生成纹理。

    1. void TextureMapSample::Init()
    2. {
    3. //create RGBA texture
    4. //生成一个纹理,将纹理 id 赋值给 m_TextureId
    5. glGenTextures(1, &m_TextureId);
    6. //将纹理 m_TextureId 绑定到类型 GL_TEXTURE_2D 纹理
    7. glBindTexture(GL_TEXTURE_2D, m_TextureId);
    8. //设置纹理 S 轴(横轴)的拉伸方式为截取
    9. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    10. //设置纹理 T 轴(纵轴)的拉伸方式为截取
    11. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    12. //设置纹理采样方式
    13. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    14. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    15. glBindTexture(GL_TEXTURE_2D, GL_NONE);
    16. char vShaderStr[] =
    17. "#version 300 es \n"
    18. "layout(location = 0) in vec4 a_position; \n"
    19. "layout(location = 1) in vec2 a_texCoord; \n"
    20. "out vec2 v_texCoord; \n"
    21. "void main() \n"
    22. "{ \n"
    23. " gl_Position = a_position; \n"
    24. " v_texCoord = a_texCoord; \n"
    25. "} \n";
    26. char fShaderStr[] =
    27. "#version 300 es \n"
    28. "precision mediump float; \n"
    29. "in vec2 v_texCoord; \n"
    30. "layout(location = 0) out vec4 outColor; \n"
    31. "uniform sampler2D s_TextureMap; \n"
    32. "void main() \n"
    33. "{ \n"
    34. " outColor = texture(s_TextureMap, v_texCoord); \n"
    35. " //outColor = texelFetch(s_TextureMap, ivec2(int(v_texCoord.x * 404.0), int(v_texCoord.y * 336.0)), 0);\n"
    36. "} \n";
    37. m_ProgramObj = GLUtils::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
    38. if (m_ProgramObj)
    39. {
    40. m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_TextureMap");
    41. }
    42. else
    43. {
    44. LOGCATE("TextureMapSample::Init create program fail");
    45. }
    46. }

            接下来定义好两个坐标系数据,以及绘制三角形的顺序等,如下:

    1. //顶点坐标
    2. GLfloat verticesCoords[] = {
    3. -1.0f, 0.5f, 0.0f, // Position 0
    4. -1.0f, -0.5f, 0.0f, // Position 1
    5. 1.0f, -0.5f, 0.0f, // Position 2
    6. 1.0f, 0.5f, 0.0f, // Position 3
    7. };
    8. //纹理坐标
    9. GLfloat textureCoords[] = {
    10. 0.0f, 0.0f, // TexCoord 0
    11. 0.0f, 1.0f, // TexCoord 1
    12. 1.0f, 1.0f, // TexCoord 2
    13. 1.0f, 0.0f // TexCoord 3
    14. };
    15. //两个三角形
    16. GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

            draw方法中加载图像数据到纹理

    1. //upload RGBA image data
    2. glActiveTexture(GL_TEXTURE0);
    3. //将纹理 m_TextureId 绑定到类型 GL_TEXTURE_2D 纹理
    4. glBindTexture(GL_TEXTURE_2D, m_TextureId);
    5. //加载 RGBA 格式的图像数据
    6. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[0]);
    7. glBindTexture(GL_TEXTURE_2D, GL_NONE);
    8. // Use the program object
    9. glUseProgram (m_ProgramObj);
    10. // Load the vertex position
    11. glVertexAttribPointer (0, 3, GL_FLOAT,
    12. GL_FALSE, 3 * sizeof (GLfloat), verticesCoords);
    13. // Load the texture coordinate
    14. glVertexAttribPointer (1, 2, GL_FLOAT,
    15. GL_FALSE, 2 * sizeof (GLfloat), textureCoords);
    16. glEnableVertexAttribArray (0);
    17. glEnableVertexAttribArray (1);
    18. // Bind the RGBA map
    19. glActiveTexture(GL_TEXTURE0);
    20. //将纹理 m_TextureId 绑定到类型 GL_TEXTURE_2D 纹理
    21. glBindTexture(GL_TEXTURE_2D, m_TextureId);
    22. // Set the RGBA map sampler to texture unit to 0
    23. glUniform1i(m_SamplerLoc, 0);
    24. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

            init初始化和draw绘制后,就能将纹理映射成功,并显示图片出来, 到这里demo基本就做完了。

    四、运行demo

            运行一下demo,可以看到效果如下:

             该demo已放到github上,有需要的可以下载看看:

    https://github.com/weekend-y/openGL_Android_demo/tree/master/BaseDemo/OpenGL_TextureMap

            

  • 相关阅读:
    主机加固如何应对数据世界的绑匪
    菜鸟学习第一天
    SpringBoot 实现数据脱敏
    【全网最细】谷歌小恐龙无敌代码它来了!
    便携式电热水壶外贸出口欧洲CE认证准备资料
    青少年python系列 45.文件操作1
    机器学习基础学习笔记
    Golang 快速上手 (3)
    Mybatis实现自定义TypeHandler对字段数据进行加解密
    Kata3.0.0 x LifseaOS x 龙蜥内核三管齐下!带你体验最新的安全容器之旅
  • 原文地址:https://blog.csdn.net/weekend_y45/article/details/126709635