本篇博客了解一下2D纹理,并完成一个绘制显示一张图片的Renderer。
2D纹理
2D纹理是OpenGL ES中最基本和常用的纹理形式。2D纹理本质上其实:是一个图像数据的二维数组。一个纹理的单独数据元素称作&34;。用2D纹理渲染时,纹理坐标用作纹理图像中的索引。2D纹理的纹理坐标用一对2D坐标(s,t)指定,有时也 称作(u,v)坐标。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0,0),也就是纹理图片的左下角,终始于(1,1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。
1、点击一个Word文档。2、点击菜单中的插入。4、选择图片。5、插入图片后右键选择这个图片,选择大小和位置。6、在弹出的窗体中选择文字环绕,根据需要选择即可。
我们为三角形指定了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角形左下角顶点的纹理坐标设置为(0,0);三角形的上顶点对应于图片的上中位置所以我们把它的纹理坐标设置为(0.5,1.0);同理右下方的顶点设置为(1,0)。我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值。
1、打开word文档,根据需要设置相应的图片。2、点击选择图片后,点击【布局】菜单。3、找到【排列】选项卡并点击,选择其中的【环绕文字】。4、调出【布局】功能对话框,选择其中的【文字环绕】功能卡。5、弹出【布局】对话。
纹理坐标看起来就像这样:
float texCoords[] = {0.0f,0.0f,// 左下角1.0f,0.0f,// 右下角0.5f,1.0f // 上中};
对纹理采样的解释非常宽松,它可以采用几种不同的插值方式。所以我们需要自己告诉OpenGL该怎样对纹理采样。
【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】
纹理环绕方式
纹理坐标的范围通常是从(0,0)到(1,1),那如果我们把纹理坐标设置在范围之外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:
前面提到的每个选项都可以使用glTexParameter*函数对单独的一个坐标轴设置(s、t(如果是使用3D纹理那么还有一个r)它们和x、y、z是等价的):
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_MIRRORED_REPEAT);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_MIRRORED_REPEAT);
第一个参数指定了纹理目标;我们使用的是2D纹理,因此纹理目标是GL_TEXTURE_2D。第二个参数需要我们指定设置的选项与应用的纹理轴。我们打算配置的是WRAP选项,并且指定S和T轴。最后一个参数需要我们传递一个环绕方式(Wrapping),在这个例子中OpenGL会给当前激活的纹理设定纹理环绕方式为GL_MIRRORED_REPEAT。
纹理过滤
纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素映射到纹理坐标。当你有一个很大的物体但是纹理的分辨率很低的时候这就变得很重要了。你可能已经猜到了,OpenGL也有对于纹理过滤(Texture Filtering)的选项。纹理过滤有很多个选项,但是现在我们只讨论最重要的两种:GL_NEAREST和GL_LINEAR。
GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。下图中你可以看到四个像素,加号代表纹理坐标。左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:
GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。下图中你可以看到返回的颜色是邻近像素的混合色:
那么这两种纹理过滤方式有怎样的视觉效果呢?让我们看看在一个很大的物体上应用一张低分辨率的纹理会发生什么吧(纹理被放大了,每个纹理像素都能看到):
设置图片环绕方式的具体操作步骤如下:1、首先在电脑的桌面上点击打开word文档。2、接着在此页面,使用鼠标左键单击选中要进行操作的图片。3、接着此时单击此页面上方的图片工具的“格式”选项卡。4、接着在此选项卡的下方。
GL_NEAREST产生了颗粒状的图案,我们能够清晰看到组成纹理的像素,而GL_LINEAR能够产生更平滑的图案,很难看出单个的纹理像素。GL_LINEAR可以产生更真实的输出,但有些开发者更喜欢8-bit风格,所以他们会用GL_NEAREST选项。
当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。我们需要使用glTexParameter*函数为放大和缩小指定过滤方式。这段代码看起来会和纹理环绕方式的设置很相似:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
多级渐远纹理
想象一下,假设我们有一个包含着上千物体的大房间,每个物体上都有纹理。有些物体会很远,但其纹理会拥有与近处物体同样高的分辨率。由于远处的物体可能只产生很少的片段,word中图片环绕方式,OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。在小物体上这会产生不真实的感觉,更不用说对它们使用高分辨率纹理浪费内存的问题了。
手工为每个纹理图像创建一系列多级渐远纹理很麻烦,幸好OpenGL有一个glGenerateMipmaps函数,在创建完一个纹理后调用它OpenGL就会承担接下来的所有工作了。
就像纹理过滤一样,我们可以使用glTexParameteri将过滤方式设置为前面四种提到的方法之一:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】
代码实现
首先,定义顶点坐标和纹理坐标
** * 顶点坐标 * (x,y,z) */private float[] POSITION_VERTEX = new float[]{0f,0f,0f,//顶点坐标V01f,1f,0f,//顶点坐标V1-1f,1f,0f, //顶点坐标V2-1f,-1f,0f,//顶点坐标V31f,-1f,0f //顶点坐标V4};/** * 纹理坐标 * (s,t) */private static final float[] TEX_VERTEX = {0.5f,0.5f,//纹理坐标V01f,0f,//纹理坐标V10f,0f,//纹理坐标V20f,1.0f,//纹理坐标V31f,1.0f//纹理坐标V4};
这里顶点坐标和纹理坐标是一一对应的,只是因为二者坐标原点不同,坐标值也不同,如下图。
** * 索引,最终绘制时通过索引从顶点数据中取出对应顶点,再按照指定的方式进行绘制 */private static final short[] VERTEX_INDEX = {0,1,2, //V0,V1,V2 三个顶点组成一个三角形0,2,3, //V0,V2,V3 三个顶点组成一个三角形0,3,4, //V0,V3,V4 三个顶点组成一个三角形0,4,1 //V0,V4,V1 三个顶点组成一个三角形};
片段着色器应该接下来会把输出变量vTexCoord作为输入变量。
我们使用GLSL内建的texture函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
public static int loadTexture(Context context,int resourceId) {final int[] textureIds = new int[1];//创建一个纹理对象GLES30.glGenTextures(1,textureIds,0);if (textureIds[0] == 0) {Log.e(TAG,&34;);return 0;}final BitmapFactory.Options options = new BitmapFactory.Options();//这里需要加载原图未经缩放的数据options.inScaled = false;final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),resourceId,options);if (bitmap == null) {Log.e(TAG,&34; + resourceId + &34;);GLES30.glDeleteTextures(1,textureIds,0);return 0;}// 绑定纹理到OpenGLGLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureIds[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到纹理中GLUtils.texImage2D(GLES30.GL_TEXTURE_2D,0,bitmap,0);// 生成MIP贴图GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);// 数据如果已经被加载进OpenGL,则可以回收该bitmapbitmap.recycle();// 取消绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,0);return textureIds[0];}
绘制
以WPS为例,设置图片紧密型环绕的方法如下:1、在文档中插入一张图片。2、选中图片后单击鼠标右键选择“其他布局选项”。3、选择“文字环绕”后选择“紧密型”,点击确定即可。WPSforAndroid是WPSOffice的安卓版,WPSOffice是。
@Overridepublic void onDrawFrame(GL10 gl) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);//使用程序片段GLES30.glUseProgram(mProgram);GLES30.glUniformMatrix4fv(uMatrixLocation,1,false,mMatrix,0);GLES30.glEnableVertexAttribArray(0);GLES30.glVertexAttribPointer(0,3,GLES30.GL_FLOAT,false,0,vertexBuffer);GLES30.glEnableVertexAttribArray(1);GLES30.glVertexAttribPointer(1,2,GLES30.GL_FLOAT,false,0,mTexVertexBuffer);GLES30.glActiveTexture(GLES30.GL_TEXTURE0);//绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureId);// 绘制GLES20.glDrawElements(GLES20.GL_TRIANGLES,VERTEX_INDEX.length,GLES20.GL_UNSIGNED_SHORT,mVertexIndexBuffer);}
最终展示:
Word中设置四周环绕图片的方法如下:1、在电脑桌面下方的任务栏的搜索栏中搜索“Word”。2、在随后打开的界面右侧点击“空白文档”,新建一个空白文档。3、在word界面左上方单击“插入”按钮,在随后弹出的菜单中选择“图片”。