在OpenGL上绘制图像时,有时候需要控制图像形状,比如圆形,方形,裁处各种形状。

如下效果

   

android opengl 修改纹理 opengl纹理大小_ci

   

android opengl 修改纹理 opengl纹理大小_#define_02

   

android opengl 修改纹理 opengl纹理大小_android opengl 修改纹理_03

最简单的方法就是在上面加个遮罩

其实遮罩也很麻烦,需要对View进行重绘。于是打算从根源出发,控制OpenGL的绘制区域。

矩形剪裁就很简单了,这里不多说了。主要说说圆形,圆形是通过无限细分来逐渐逼近圆形。细分等分越大,线条看起来越光滑。下面是10等分,51等分和100等分的效果图。

android opengl 修改纹理 opengl纹理大小_android opengl 修改纹理_04

   

android opengl 修改纹理 opengl纹理大小_android opengl 修改纹理_05

   

android opengl 修改纹理 opengl纹理大小_ide_06

OpenGL中,纹理和顶点坐标是对应的。只改变定点坐标的话,得到剪裁后的结果会偏移。所以需要同时改变定点坐标和纹理坐标,来控制绘制区域。

其中,定点坐标的坐标系是以屏幕中心为原点的普通坐标系,最大长度为单位长度1。而纹理坐标有点不同,其原点在屏幕左下角,最大长度为单位长度1。

所以,顶点对应的纹理坐标只需要做相应的坐标转换。

基本关系是:

for(GLuint i = 0; i < num_vertex ; i++)  
        {  
            texcoord[0] = (cos(delta_angle * i) + 1.0) * 0.5;  
            texcoord[1] = (sin(delta_angle * i) + 1.0) * 0.5;  
            glTexCoord2fv(texcoord);  
  
            vertex[0] = cos(delta_angle * i) * radius;  
            vertex[1] = sin(delta_angle * i) * radius;  
            vertex[2] = 0.0;  
            vertex[3] = 1.0;  
            glVertex4fv(vertex);  
        }

矩形和圆形的处理如下:

AGLKVertexAttribArrayBuffer *temvertexBuffer;
    if (mask == ovglview_mask_none) {
        numOfVertext = 4;
        drawModel = GL_TRIANGLE_STRIP;
        
        temvertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]
                                                        initWithAttribStride:sizeof(SceneVertex)
                                                        numberOfVertices:sizeof(vertices) / sizeof(SceneVertex)
                                                        bytes:vertices
                                                        usage:GL_STATIC_DRAW];
    }
    else if (mask == ovglview_mask_rectangle){
        numOfVertext = 4;
        drawModel = GL_TRIANGLE_STRIP;
        SceneVertex temVertex[4];
        for (int i = 0; i < 4; i++) {
            temVertex[i].textureCoords.s = vertices[i].textureCoords.s/scale_1 + (1.0f - 1.0f/scale_1)/2.0;
            temVertex[i].textureCoords.t = vertices[i].textureCoords.t/scale_1 + (1.0f - 1.0f/scale_1)/2.0;
            temVertex[i].positionCoords.x = vertices[i].positionCoords.x/scale_1;
            temVertex[i].positionCoords.y = vertices[i].positionCoords.y/scale_1;
            temVertex[i].positionCoords.z = vertices[i].positionCoords.z/scale_1;
        }
        temvertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]
                                                       initWithAttribStride:sizeof(SceneVertex)
                                                       numberOfVertices:sizeof(temVertex) / sizeof(SceneVertex)
                                                       bytes:temVertex
                                                       usage:GL_STATIC_DRAW];
    }
    else if (mask == ovglview_mask_circle){
        SceneVertex circleVertex[100];
        GLsizei num = 100;
        GLfloat angle = 2*PI/num;
        GLfloat r = 1.0/scale_1;
        for (GLint i = 0; i < num; i++) {
            circleVertex[i].textureCoords.s = 1.0 - (GLfloat)((r * cos(angle * i)) +1.0)*0.5;
            circleVertex[i].textureCoords.t = (GLfloat)((r * sin(angle * i)) +1.0)*0.5;
            circleVertex[i].positionCoords.x = (GLfloat)(r * cos(angle * i));
            circleVertex[i].positionCoords.y = (GLfloat)(r * sin(angle * i));
            circleVertex[i].positionCoords.z = 0;
        }
        temvertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]
                                                        initWithAttribStride:sizeof(SceneVertex)
                                                        numberOfVertices:sizeof(circleVertex) / sizeof(SceneVertex)
                                                        bytes:circleVertex
                                                        usage:GL_STATIC_DRAW];
        numOfVertext = num;
        drawModel = GL_TRIANGLE_FAN;
    }

还需要强调的是,顶点的绘制模式也需要随之改变

常见的模式有三种:

#define GL_TRIANGLES                     0x0004
#define GL_TRIANGLE_STRIP                0x0005
#define GL_TRIANGLE_FAN                  0x0006

OpenGL中绘制的是三角面片(基本单位)图像是由若干三角面片组成的

三种绘制模式如图所示:

GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);

android opengl 修改纹理 opengl纹理大小_ide_07

 

图片来自【OpenGL】理解GL_TRIANGLE_STRIP等绘制三角形序列的三种方式

GL_TRIANGLES

是以每三个顶点绘制一个三角形。第一个三角形使用顶点v0,v1,v2,第二个使用v3,v4,v5,以此类推。如果顶点的个数n不是3的倍数,那么最后的1个或者2个顶点会被忽略。

这是最基本的绘制方式,单纯以三个顶点来绘制三角形,但是比较浪费资源,其顶点不能重用。

GL_TRIANGLE_STRIP

是可以进行顶点重用的,规则有点拗口:

构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):

如果当前顶点是奇数:

组成三角形的顶点排列顺序:T = [n-1 n-2 n].

如果当前顶点是偶数:

组成三角形的顶点排列顺序:T = [n-2 n-21 n].

以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。

这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。对于某些操作,维持方向是很重要的,比如剔除。

注意:顶点个数n至少要大于3,否则不能绘制任何三角形。

例如:一个矩形由两个三角形组成,按照GL_TRIANGLES模式需要6个顶点,而按照GL_TRIANGLE_STRIP只需要4个顶点,但是需要注意顶点的顺序

GL_TRIANGLE_FAN

顾名思义,有个fan,是和扇形相关的,方便绘制扇形。可以以某一点为圆心进行绘制

它的三角形的顶点排列顺序是T = [n-1 n-2 n].各三角形形成一个扇形序列。

所以,在改变顶点坐标和纹理坐标的同时,还需要改变绘制模式

个人见解,有误之处还请指正