OpenGL ES纹理详解

2022-10-19,,,

这篇文章主要为大家详细介绍了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方法定位。

代码下载

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

您可能感兴趣的文章:

  • OpenGL关于glStencilFuncSeparate()和glStencilFunc()函数的区别讲解
  • SDL2和OpenGL使用踩坑笔记经验分享
  • Android利用OpenGLES绘制天空盒实例教程
  • android使用OPENGL ES绘制圆柱体
  • opengl实现任意两点间画圆柱体
  • OpenGL ES透视投影实现方法(四)
  • OpenGL ES正交投影实现方法(三)
  • OpenGL ES着色器使用详解(二)
  • OpenGL ES渲染管线概述(一)
  • OpenGL中的glutInitDisplayMode()函数的理解

《OpenGL ES纹理详解.doc》

下载本文的Word格式文档,以方便收藏与打印。