上一篇文章咱们经过金字塔延伸到了正方体,然后到这篇正方体每一个面贴一张图。 先看作用图:Demo

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

接下来让咱们开始学习OpenGL 一个重要‼️的知识点:纹路 学习博客:半纸渊–根底纹路 前言:之前咱们说过纹路能够简略理解为图片,可是纹路不简简略单图片。

  • 1. Texture 是什么?

Texture 纹路,便是一堆被精心排列过的像素; Texture 在 OpenGL 里边有许多品种,但在 ES 版别中就两种:Texture_2D 、 Texture_CubeMap

  • Texture_2D:

便是 {x, y} 二维空间下的像素呈现,也便是说,由作用图上可知,很难做到使正方体的六个面呈现不同的像素组合;图片处理一般都运用这个形式;[x 、y 归于 [0, 1] 这个规模]

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

  • Texture_CubeMap:

便是 { x, y, z } 三维空间下的像素呈现,也就如作用图中演示的正方体的六个面能够呈现不同的像素组合;它一般是用于做环境贴图——便是制造一个环境,让 3D 模型如同置身于实在环境中【卡通环境中也行】。[x、y、z 归于 [-1, 1] 这个规模,便是与 Vertex Position 的值规模共同]

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

注:上面提到的一切坐标规模是指有用烘托规模,也便是说你假如提供的纹路坐标超出了这个规模也没有问题,只不过超出的部分就不烘托了;

极点数据表明如下:

  • Texture_2D:
 //------------- 正方体 -------------
        let attrArr: [GLfloat] = [
            // 极点:(x, y, z)      色彩:(r, g, b)      纹路: (s, t)
            // 前面
            -0.5, 0.5, 0.5,        1.0, 1.0, 1.0,       0.0, 0.0, // 前左上 0
            -0.5, -0.5, 0.5,       1.0, 1.0, 1.0,       0.0, 1.0, // 前左下 1
            0.5, -0.5, 0.5,        1.0, 1.0, 1.0,       1.0, 1.0, // 前右下 2
            0.5, 0.5, 0.5,         1.0, 1.0, 1.0,       1.0, 0.0, // 前右上 3
...
]
  • Texture_CubeMap:
        let attrArr: [GLfloat] = [
            // 极点:(x, y, z)      色彩:(r, g, b)      纹路: (s, t, p)
            // 前面
            -1.0, 1.0, 1.0,        1.0, 0.0, 0.0,       -1.0, 1.0, 1.0, // 前左上 0
            -1.0, -1.0, 1.0,       0.0, 1.0, 0.0,       -1.0, -1.0, 1.0, // 前左下 1
            1.0, -1.0, 1.0,        0.0, 0.0, 1.0,       1.0, -1.0, 1.0, // 前右下 2
            1.0, 1.0, 1.0,         1.0, 1.0, 1.0,       1.0, 1.0, 1.0, // 前右上 3
...
]
⚠️ps: CubeMap 里边的纹路坐标和极点数据是相同的
  • 加载CubeMap纹路

CubeMap共有6个面,然后分别设置 GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A

代码如下:注意⚠️:这儿是cubeMap 6张图片的宽高要共同

    //7.1 设置立方体纹路
    func setupCubeTexture() {
        //7.绑定纹路到默许的纹路ID(这儿只要一张图片,故而适当于默许于片元着色器里边的us2d_texture)
        glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
        for i in 0..<6 {
            let spriteImage: CGImage = UIImage(named: "timg-\(i+1)")!.cgImage!
            //2.读取图片的巨细:宽和高 注意⚠️:这儿是cubeMap 6张图片的宽高要共同
            let width = 512//spriteImage.width
            let height = 512//spriteImage.height
            //3.获取图片字节数: 宽x高x4(RGBA)
    //        let spriteData: UnsafeMutablePointer = UnsafeMutablePointer<GLbyte>.allocate(capacity: MemoryLayout<GLbyte>.size * width * height * 4)
            let spriteData: UnsafeMutableRawPointer = calloc(width * height * 4, MemoryLayout<GLbyte>.size)
            //4.创立上下文
            /*
             参数1:data,指向要烘托的制作图像的内存地址
             参数2:width,bitmap的宽度,单位为像素
             参数3:height,bitmap的高度,单位为像素
             参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8
             参数5:bytesPerRow,bitmap的每一行的内存所占的比特数
             参数6:colorSpace,bitmap上运用的色彩空间  kCGImageAlphaPremultipliedLast:RGBA
             let colorSpace = CGColorSpaceCreateDeviceRGB()
             */
            let spriteContext: CGContext = CGContext(data: spriteData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: spriteImage.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
            //5.在CGContextRef上绘图
            let rect = CGRect(x: 0, y: 0, width: width, height: height)
            spriteContext.draw(spriteImage, in: rect)
            //载入纹路2D数据 便是加载纹路像素到 GPU 的办法
            /*
             参数1:纹路形式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
             参数2:加载的层次,一般设置为0
             参数3:纹路的色彩值GL_RGBA
             参数4:宽
             参数5:高
             参数6:border,边界宽度
             参数7:format
             参数8:type
             参数9:纹路数据
             */
            glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), spriteData)
            //开释spriteData
            free(spriteData)
        }
        //设置纹路特点
        /*
         参数1:纹路维度
         参数2:线性过滤、为s,t坐标设置形式
         参数3:wrapMode,环绕形式
         */
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
        //绑定纹路
        /*
         参数1:纹路维度
         参数2:纹路ID,由于只要一个纹路,给0就能够了。
         */
        glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
    }

由于咱们的设置的纹路坐标由两位:[s, t] –> [s, t, p],所以着色器中的纹路坐标要做相应的改变,还有烘托那里读取数据的时分也做相应改变。

  • 极点着色器代码:
//纹路坐标vec2 --> vec3
attribute vec4 position;
attribute vec4 positionColor; //极点色彩
attribute vec3 textCoordinate; //纹路坐标
uniform mat4 projectionMatrix; //投影矩阵
uniform mat4 modelViewMatrix;  //模型视图矩阵
varying lowp vec4 varyColor; //极点色彩
varying lowp vec3 varyTextCoord; //传递给片元着色器纹路坐标
void main()
{
    varyColor = positionColor;
    varyTextCoord = textCoordinate;
    vec4 vPos;
    vPos = projectionMatrix * modelViewMatrix * position;
    gl_Position = vPos;
}
  • 片元着色器代码:
//纹路坐标vec2 --> vec3
varying lowp vec4 varyColor; //极点色彩
varying lowp vec3 varyTextCoord; //极点着色器传递过来的纹路坐标
//uniform sampler2D colorMap; //纹路
uniform samplerCube us2d_texture;
void main()
{
    gl_FragColor = textureCube(us2d_texture, varyTextCoord) * varyColor;
}

烘托代码:

步长和纹路坐标做相应的调整即可,就不贴了

到此正方体贴图工作就完成了。详细请查看源码


可是从学习的博客半纸渊–根底纹路,看到他能完成下图像魔方的作用,已然都做到多面贴图了,所以也想试试看。

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

经过查看他的源码,这种完成办法也是:glTexImage2D,只不过最后一个参数数据是个色彩数组

  • 加载纹路的代码就变成这样:
    //7.2 设置立方体像素纹路
    func setupCubePixelsTexture() {
        //7.绑定纹路到默许的纹路ID(这儿只要一张图片,故而适当于默许于片元着色器里边的us2d_texture)
        glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
        for i in 0..<6 {
            glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, 2, 2, 0, GLenum(GL_RGBA), GLenum(GL_FLOAT), texCubemapPixelDatas[i])
        }
        //设置纹路特点
        /*
         参数1:纹路维度
         参数2:线性过滤、为s,t坐标设置形式
         参数3:wrapMode,环绕形式
         */
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
        glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
        //绑定纹路
        /*
         参数1:纹路维度
         参数2:纹路ID,由于只要一个纹路,给0就能够了。
         */
        glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
    }

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

这并不是咱们想要的作用,并且连方格都没有显现出来,尽管中心看似有分割线。可是离作用图还是天差地别的。怎么回事呢?对比了一下发现在过滤办法不同:GL_LINEAR 和 GL_NEAREST

摘抄自:mChenys — 六、OpenGL ES纹路的运用 当纹路巨细要被扩展或许缩小的时分,咱们需要运用纹路过滤清晰说明会产生什么,当咱们在烘托表面上制作一个纹路时,那个纹路的纹路元素或许无法精确地映射到OpenGL生成的片段上,有2种状况:缩小或许扩大。 当咱们极力把几个纹路元素挤进一个片段时,缩小就会产生了,当把一个纹路元素扩展到许多片段时,扩大就产生了。 针对每一种状况,咱们都能够配置OpenGL运用一个纹路过滤器,我会运用下面的图像阐述每一种过滤形式:

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

  • GL_NEAREST(也叫附近过滤,Nearest Neighbor Filtering)是OpenGL默许的纹路过滤办法。当设置为GL_NEAREST的时分,OpenGL会挑选中心点最接近纹路坐标的那个像素。下图中你能够看到四个像素,加号代表纹路坐标。左上角那个纹路像素的中心距离纹路坐标最近,所以它会被挑选为样本色彩:

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

这种办法为每个片段挑选最近的纹路元素,当扩大纹路时它的锯齿作用看起来适当明显,每个纹路单元都清楚地显现为一个小方块。

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

当咱们缩小纹路时,由于没有满足的片段来制作一切的纹路单元,许多细节将会丢失。

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

  • GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹路坐标附近的纹路像素,计算出一个插值,近似出这些纹路像素之间的色彩。一个纹路像素的中心距离纹路坐标越近,那么这个纹路像素的色彩对最终的样本色彩的奉献越大。下图中你能够看到返回的色彩是附近像素的混合色:

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

线性过滤运用双线插值滑润像素之间的过渡,而不是每个片段运用最近的纹路元素,OpenGL会运用四个邻接的纹路元素,并在他们之间用一个线性差值算法做差值,这个算法与前面介绍滑润着色的算法相同,之所以叫它双线性,是由于它是沿两个维度差值的,它会比近邻过滤要滑润许多,但还是会有一些锯齿显现出来,由于咱们把这个纹路扩展得太多了,可是锯齿没有最近邻过滤那么明显。

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

PS:纹路扩大时运用线性过滤(GL_LINEAR),缩小时用附近过滤(GL_NEAREST)

然后咱们修正过滤办法为:附近过滤(GL_NEAREST) 作用如下:

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析

这儿看到已经差不多了和他的相同了。可是总觉得怪怪的。便是每一个面都会有一块是黑色的,而对照的却不是这样的。然后再去仔细看了看。后面发现本来还是在这个办法上呈现了问题:

glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, 2, 2, 0, GLenum(GL_RGBA), GLenum(GL_FLOAT), texCubemapPixelDatas[i]) //载入纹路2D数据 便是加载纹路像素到 GPU 的办法 参数1:纹路形式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D 参数2:加载的层次,一般设置为0 参数3:纹路的色彩值GL_RGBA 参数4:宽 参数5:高 参数6:border,边界宽度 参数7:format 参数8:type 参数9:纹路数据 这儿的(参数3:纹路的色彩值,参数7:format)咱们传的是GL_RGBA,而数组里边只要(r, g, b)并没有a,所以呈现取值有问题吧。改成 GL_RGB

运转成果:

iOS视觉-- (05) OpenGL ES+GLSL实现正方体贴6张图解析