OpenGL ES纹理详解

使用前面学过的技术已经可以利用OpenGL ES构建立体图形,并通过顶点着色器和片元着色器对其进行各种变化呢和光照等效果使得三维效果更加真实,实际上我看看到很多的3D游戏漂亮多了,那是因为有各种各样的漂亮的图像带给人很多视觉盛宴,这篇文章在前面的基础上,增加物体的表面贴图,使得物体更加好看。

纹理概念

纹理用来表示图像照片或者说一系列的数据,使用纹理可以使物体用用更多的细节。OpenGL ES 2.0 中有两种贴图:二维纹理和立方体纹理。

每个二维纹理都由许多小的纹理元素组成,类似与片元和像素,使用纹理最简单的方式就是直接从一个图像加载数据。在OpenGL中规定纹理图像的左下角由stst坐标(0.0,0.0)指定,右上角由stst坐标(1.0,1.0)指定,不过超过1.0的坐标也是允许的,在该区间之外的纹理在读取时的时候由纹理拉伸模式决定。

OpenGL ES 2.0不必是正方形,但是每个维度都应该是2的幂

在Android中使用的OpenGL ES的纹理坐标系跟官方的纹理坐标系统不一样,在Android中使用官方的纹理坐标系统,得到的结果是相反的,而是左上角是stst坐标(0.0,0.0)点,右下角是stst坐标(1.0,1.0)点。

二维纹理映射的原理

使用纹理就是在纹理图中进行采样,因此需要将选定的纹理坐标穿进顶点着色器,经过插值在片元着色器中从纹理图中的指定位置采样即可,纹理图的数据通过往片元插值器传递纹理单元指定的。

纹理对象和纹理加载

创建一个纹理对象,保存渲染所需的纹理数据,例如图像数据、过滤模式、包装模式。创建生成纹理对象的函数

public static native void glGenTextures(
    int n, // 指定要生成的纹理对象的数量
    int[] textures, // 保存纹理对象ID的数组
    int offset
  );

纹理对象在应用程序中不再使用时,需要删除。

public static native void glDeleteTextures(
    int n, // 指定要删除的纹理数量
    int[] textures, // 保存待删除的纹理ID的数组
    int offset
  );

纹理对象的 ID 必须是 glGenTextures 产生的,一旦生成纹理ID,就必须绑定纹理对象才能继续进行后续的操作。后续的操作将影响绑定的纹理对象。一旦纹理被绑定到一个特定的纹理目标,再删除之前就一直保持着绑定状态。

public static native void glBindTexture(
    int target, // 绑定纹理对象到目标 GL_TEXTURE_2D 或 GL_TEXTURE_CUBE_MAP
    int texture // 要绑定的纹理对象ID
  );

激活某个纹理单元

public static native void glActiveTexture(
    int texture // 要激活的纹理单元
  );

对这两个函数的理解:显卡中有N个纹理单元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2…),每个纹理单元中保存着很多纹理目标(targetTexture1D,targetTexture2D,targetTexture3D,targetTextureCube…),OpenGL ES 2.0貌似只支持了targetTexture2D和targetTextureCube。

纹理单元TextureUnit的定义如下

struct TextureUnit
{
  GLuint targetTexture1D;
  GLuint targetTexture2D;
  GLuint targetTexture3D;
  GLuint targetTextureCube;
  ...
};

glActiveTexture函数就是设置当前活动的纹理单元

TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
GLuint currentTextureUnit = 0;
// ...
void glActiveTexture(GLenum textureUnit)
{
  currentTextureUnit = textureUnit - GL_TEXTURE0 ;
}

glBindTexture函数就是将纹理对象ID赋值给当前活动的纹理单元的对应的目标纹理。

void glBindTexture(GLenum textureTarget, GLuint textureObject)
{
  TextureUnit *texUnit = &textureUnits[currentTextureUnit];
  switch(textureTarget)
  {
  case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break;
  case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break;
  case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break;
  case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break;
  }
}

获取一副图片的纹理数据

public static void texImage2D(int target, // 常数GL_TEXTURE_2D
               int level, // 表示多级分辨率的纹理图像的级数,若只有一种分辨率,则level设为0。
               Bitmap bitmap,
               int border // 边框,一般设为0
               )

其他纹理选项的设置使用glTexParameterf系列函数

public static native void glTexParameterf(
    int target,
    int pname, // 设定的参数,可以是GL_TEXTURE_MAG_FILTER,GL_TEXTURE_MIN_FILTER,GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T
    float param // 参数对应的值
  );

应用纹理的例子

对前面的立方体的每个面应用一张图片作为纹理贴图,效果图(这个纹理图是哪个老师来着?)

Rectangle.java

public class Rectangle {
  private FloatBuffer mVertexBuffer;
  private int mProgram;
  private int mPositionHandle;
  private int muMVPMatrixHandle;
  private int mColorHandle;
  private int muMMatrixHandle;
  private int muLightLocationHandle;
  private int mTextureCoordHandle;
  private int textureId;
  private int muTextureHandle;

  private Context mContext;
  public Rectangle(Context context) {
    this.mContext = context;
    initVetexData();
  }

  public void initVetexData() {
    float vertices[] = new float[] {
        // 顶点  颜色   纹理坐标
        //前面
        0, 0, 1, 1,1,1,0, 0.5f, 0.5f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        -1, 1, 1, 1,0,0,0, 0.0f, 0.0f,
        0, 0, 1, 1,1,1,0, 0.5f, 0.5f,
        -1, 1, 1, 1,0,0,0, 0.0f, 0.0f,
        -1,-1, 1, 1,0,0,0, 0.0f, 1.0f,
        0, 0, 1, 1,1,1,0, 0.5f, 0.5f,
        -1,-1, 1, 1,0,0,0, 0.0f, 1.0f,
        1,-1, 1, 1,0,0,0, 1.0f, 1.0f,
        0, 0, 1, 1,1,1,0, 0.5f, 0.5f,
        1,-1, 1, 1,0,0,0, 1.0f, 1.0f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        //后面
        0, 0,-1, 1,1,1,0, 0.5f, 0.5f,
        1, 1,-1, 1,0,0,0, 1.0f, 0.0f,
        1,-1,-1, 1,0,0,0,  0.0f, 0.0f,
        0, 0,-1, 1,1,1,0, 0.5f, 0.5f,
        1,-1,-1, 1,0,0,0,  0.0f, 0.0f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        0, 0,-1, 1,1,1,0, 0.5f, 0.5f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        -1, 1,-1, 1,0,0,0, 1.0f, 1.0f,
        0, 0,-1, 1,1,1,0, 0.5f, 0.5f,
        -1, 1,-1, 1,0,0,0, 1.0f, 1.0f,
        1, 1,-1, 1,0,0,0, 1.0f, 0.0f,
        //左面
        -1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        -1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        -1, 1,-1, 1,0,0,0, 0.0f, 0.0f,
        -1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        -1, 1,-1, 1,0,0,0, 0.0f, 0.0f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        -1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        -1,-1, 1, 1,0,0,0, 1.0f, 1.0f,
        -1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        -1,-1, 1, 1,0,0,0, 1.0f, 1.0f,
        -1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        //右面
        1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        1,-1, 1, 1,0,0,0, 0.0f, 0.0f,
        1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        1,-1, 1, 1,0,0,0, 0.0f, 0.0f,
        1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        1, 1,-1, 1,0,0,0, 1.0f, 1.0f,
        1, 0, 0, 1,1,1,0, 0.5f, 0.5f,
        1, 1,-1, 1,0,0,0, 1.0f, 1.0f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        //上面
        0, 1, 0, 1,1,1,0, 0.5f, 0.5f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        1, 1,-1, 1,0,0,0,  0.0f, 0.0f,
        0, 1, 0, 1,1,1,0, 0.5f, 0.5f,
        1, 1,-1, 1,0,0,0,  0.0f, 0.0f,
        -1, 1,-1, 1,0,0,0, 0.0f, 1.0f,
        0, 1, 0, 1,1,1,0, 0.5f, 0.5f,
        -1, 1,-1, 1,0,0,0, 0.0f, 1.0f,
        -1, 1, 1, 1,0,0,0, 1.0f, 1.0f,
        0, 1, 0, 1,1,1,0, 0.5f, 0.5f,
        -1, 1, 1, 1,0,0,0, 1.0f, 1.0f,
        1, 1, 1, 1,0,0,0, 1.0f, 0.0f,
        //下面
        0,-1, 0, 1,1,1,0, 0.5f, 0.5f,
        1,-1, 1, 1,0,0,0, 1.0f, 0.0f,
        -1,-1, 1, 1,0,0,0, 0.0f, 0.0f,
        0,-1, 0, 1,1,1,0, 0.5f, 0.5f,
        -1,-1, 1, 1,0,0,0, 0.0f, 0.0f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        0,-1, 0, 1,1,1,0, 0.5f, 0.5f,
        -1,-1,-1, 1,0,0,0, 0.0f, 1.0f,
        1,-1,-1, 1,0,0,0, 1.0f, 1.0f,
        0,-1, 0, 1,1,1,0, 0.5f, 0.5f,
        1,-1,-1, 1,0,0,0, 1.0f, 1.0f,
        1,-1, 1, 1,0,0,0, 1.0f, 0.0f
      };
    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
    vbb.order(ByteOrder.nativeOrder());
    mVertexBuffer = vbb.asFloatBuffer();
    mVertexBuffer.put(vertices);
    mVertexBuffer.position(0);

    int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();
    GLES20.glAttachShader(mProgram, vertexShader);
    GLES20.glAttachShader(mProgram, fragmentShader);
    GLES20.glLinkProgram(mProgram);

    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
    mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");

    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
    muLightLocationHandle = GLES20.glGetUniformLocation(mProgram, "uLightLocation");
    muTextureHandle = GLES20.glGetUniformLocation(mProgram, "uTexture");
    initTexture();
  }

  // 初始化纹理
  public void initTexture() {
    int [] textures = new int[1];
    GLES20.glGenTextures(1, textures, 0);
    textureId = textures[0];
    // 激活纹理单元,默认激活的就是0号纹理单元
    //GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    // 将纹理对象ID绑定到当前活动的纹理单元0上的GL_TEXTURE_2D目标
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    // 后面对纹理的设置都是对绑定了的纹理所生效的
    //缩小采样使用最近点采样
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
    //缩小采样使用最近点采样
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
    //纹理包裹拉伸方式在st轴采用截取拉伸方式,这些设置指的是对坐标范围超过1的部分的限制
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);

    Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    // 图片已经加载到了显存,可以回收
    bitmap.recycle();
  }

  public void draw() {
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 12*6);
  }

  public void setValue(float[] mvpMatrix, float[] mMatrix) {
    GLES20.glUseProgram(mProgram);
    mVertexBuffer.position(0);
    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer);
    mVertexBuffer.position(3);
    GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer);
    mVertexBuffer.position(7);
    GLES20.glVertexAttribPointer(mTextureCoordHandle, 2, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer);
    GLES20.glEnableVertexAttribArray(mPositionHandle);
    GLES20.glEnableVertexAttribArray(mColorHandle);
    GLES20.glEnableVertexAttribArray(mTextureCoordHandle);

    GLES20.glUniform3f(muLightLocationHandle, 0, 0, 20);
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
    GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, mMatrix, 0);
    // 将使用的纹理单元0传递给片元着色器
    GLES20.glUniform1i(muTextureHandle, 0);
  }

  private int loaderShader(int type, String shaderCode) {
    int shader = GLES20.glCreateShader(type);
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);
    return shader;
  }

  private String vertexShaderCode = "uniform mat4 uMVPMatrix;"
      + "attribute vec2 aTextureCoord;"
      + "varying vec2 vTextureCoord;"
      + "uniform mat4 uMMatrix;"
      + "uniform vec3 uLightLocation;"
      + "attribute vec4 aColor;"
      + "varying vec4 vColor;"
      + "varying vec4 vDiffuse;"
      + "attribute vec3 aPosition;"
      + "void main(){"
      + "vec3 normalVectorOrigin = aPosition;"
      + "vec3 normalVector = normalize((uMMatrix*vec4(normalVectorOrigin,1)).xyz);"
      + "vec3 vectorLight = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);"
      + "float factor = max(0.0, dot(normalVector, vectorLight));"
      + "vDiffuse = factor*vec4(1,1,1,1.0);"
      + "gl_Position = uMVPMatrix * vec4(aPosition,1);"
      + "vColor = aColor;"
      + "vTextureCoord = aTextureCoord;" // 将纹理坐标传到片元着色器,得到更多的插值纹理坐标
      + "}";

  private String fragmentShaderCode = "precision mediump float;"
      + "uniform sampler2D uTexture;" // 这个uniform变量表示了纹理数据,从java中传过来的是所在的纹理单元编号
      + "varying vec2 vTextureCoord;"
      + "varying vec4 vColor;"
      + "varying vec4 vDiffuse;"
      + "void main(){"
      + "gl_FragColor = (vColor*vDiffuse + vColor*vec4(0.6,0.6,0.6,1))*texture2D(uTexture, vTextureCoord);" // 在纹理的基础上还考虑到光照,texture2D函数用于纹理采样
      + "}";
}

需要注意的还是传入的顶点的时候数组里面包含了顶点、颜色和纹理坐标,因此要用ByteBuffer的position方法定位。

代码下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • OpenGL中的glutInitDisplayMode()函数的理解

    OpenGL中的glutInitDisplayMode()函数的作用主要是在创建窗口的时候,指定其显示模式的类型. 函数原型为:void glutInitDisplayMode(unsigned int mode); mode参数是一个GLUT库里预定义的可能的布尔组合.你使用mode去指定颜色模式,数量和缓冲区类型. 其中大部分情况下使用的参数为: GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL 颜色模式:GLUT_RGBA表示颜色模式,

  • SDL2和OpenGL使用踩坑笔记经验分享

    SDL + OpenGL使用笔记 LFTK 是一个嵌入式GUI,为了开发方便,需要提供PC运行环境.我选择了SDL2+OpenGL+nanovg来实现底层的渲染,让LFTK可以运行在各个平台上.GLFW+OpenGL也是一个不错的选择,但是GLFW没有Android和iOS的移植,而且没有提供原生输入法的支持.LFTK虽然最初是为嵌入式系统而生,但也有一个小目标:可以用于开发嵌入式系统,也可以开发PC软件和移动APP,所以最后选择了SDL2+OpenGL+nanovg.在使用SDL2+OpenG

  • android使用OPENGL ES绘制圆柱体

    本文实例为大家分享了android使用OPENGL ES绘制圆柱体的具体代码,供大家参考,具体内容如下 效果图: 编写jiem.java *指定屏幕所要显示的假面,并对见.界面进行相关设置     *为Activity设置恢复处理,当Acitvity恢复设置时显示界面同样应该恢复     *当Activity暂停设置时,显示界面同样应该暂停 package com.scout.eeeeeee; import android.app.Activity; import android.os.Bund

  • OpenGL ES着色器使用详解(二)

    本文介绍了OpenGL ES着色器使用的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.着色器语言 着色器语言是一种高级图形编程语言,和C/C++语言很类似,但存在很大差别,比如,不支持double,byte ,short,不支持unin,enum,unsigned以及位运算等,但其加入了很多原生的数据类型,如向量,矩阵等. 数据类型可分为标量.向量.矩阵.采样器.结构体.数组等 向量 向量传递参数,如果只提供一个标量,这个值用于设置所有向量的值:如果输入是多个标量或者是矢量,从左到

  • OpenGL ES渲染管线概述(一)

    渲染管线一般是由显示芯片GPU内部处理图形信号的并行处理单元组成,这些并行处理单元之间是独立的,从另一个角度看,渲染管线实际上也是一系列绘制过程,这一系列过程的输入是待绘制物体的相关描述信息,输出的是要显示的图像帧数据. OpenGL ES管线主要包括: 读取顶点数据->顶点着色器->组装图元->光栅化图元->片元着色器->写入帧缓冲区->显示到屏幕上 读取顶点数据指的是将待绘制的图形的顶点数据传递给渲染管线中. 顶点着色器最终生成每个定点的最终位置,执行顶点的各种变换

  • OpenGL ES正交投影实现方法(三)

    本文实例为大家分享了OpenGL ES正交投影展示的具体代码,供大家参考,具体内容如下 绘制正方形 在最开始绘制的六边形里面好像看起来挺容易的,也没有出现什么问题,接下来不妨忘记前面绘制六边形的代码,让我们按照自己的理解来绘制一个简单的正方形. 按照我的理解,要想在屏幕中间显示一个正方形,效果如下图所示 应该创建的数据如下图所示 即传给渲染管线的顶点数据如下图: float[] vertexArray = new float[] { (float) -0.5, (float) -0.5, 0,

  • OpenGL关于glStencilFuncSeparate()和glStencilFunc()函数的区别讲解

    glStencilFunc()函数是OpenGL提供的对模板缓冲区进行控制的命令,这是OpenGL2.0之前使用的函数,其函数原型为 void glStencilFunc(GLenum func, GLint ref, GLuint mask). func指定比较函数,它指定了测试通过的条件,其取值可以是:(为方便表示,参考值为refValue, 缓冲区值bufferValue) GL_NEVER                        总是不通过测试 GL_ALWAYS        

  • Android利用OpenGLES绘制天空盒实例教程

    前言 天空盒这个效果最早是在腾讯的实景地图里看到的,当时觉得很牛逼,但是没有想过自己去实现以下.最近这段时间对opengl很有兴趣,顺便就搞了这个天空盒,话不多说,先上效果. 天空盒的原理就是在三维空间中放置一个正方体,然后将我们的相机放置在正方体内,当我们的视点转动,相机跟着转动.我们就可以看到相应的景色的变换了,天空盒本质上是一个立方体. OpenGL 关于什么是OpenGL,什么是OpenGLES就不细说了,不了解的就自行百度吧,我们主要是关注代码.整个项目采用了Kotlin + Ndk的

  • opengl实现任意两点间画圆柱体

    本文实例为大家分享了opengl实现任意两点间画圆柱体的具体代码,供大家参考,具体内容如下 1.问题提出 两点间画线简单: glBegin(GL_LINES);  //注意是LINES不是LINE,这个错误一定要注意. glVertexf(x1, y1, z1); glVertexf(x2, y2, z2); glEnd(); 画线函数不会影响opengl的矩阵堆栈. 但是很多时候线条效果会比较差,比如我要做一个骨骼动画,关节点间的骨头用线条太难看,即使使用glLineWidth设置线宽,视觉效

  • OpenGL ES透视投影实现方法(四)

    在之前的学习中,我们知道了一个顶点要想显示到屏幕上,它的x.y.z分量都要在[-1,1]之间,我们回顾一下渲染管线的图元装配阶段,它实际上做了以下几件事:剪裁坐标.透视分割.视口变换.图元装配的输入是顶点着色器的输出,抓哟是物体坐标gl_Position,之后到光栅化阶段. 图元装配 剪裁坐标 当顶点着色器写入一个值到gl_Position时,这个点要求必须在剪裁空间中,即它的x.y.z坐标必须在[-w,w]之间,任何这个范围之外的点都是不可见的. 这里需要注意以下,对于attribute类型的

随机推荐