上一篇文章咱们经过金字塔延伸到了正方体,然后到这篇正方体每一个面贴一张图。 先看作用图:Demo
接下来让咱们开始学习OpenGL 一个重要‼️的知识点:纹路 学习博客:半纸渊–根底纹路 前言:之前咱们说过纹路能够简略理解为图片,可是纹路不简简略单图片。
-
1. Texture 是什么?
Texture 纹路,便是一堆被精心排列过的像素; Texture 在 OpenGL 里边有许多品种,但在 ES 版别中就两种:Texture_2D 、 Texture_CubeMap
-
Texture_2D:
便是 {x, y} 二维空间下的像素呈现,也便是说,由作用图上可知,很难做到使正方体的六个面呈现不同的像素组合;图片处理一般都运用这个形式;[x 、y 归于 [0, 1] 这个规模]
-
Texture_CubeMap:
便是 { x, y, z } 三维空间下的像素呈现,也就如作用图中演示的正方体的六个面能够呈现不同的像素组合;它一般是用于做环境贴图——便是制造一个环境,让 3D 模型如同置身于实在环境中【卡通环境中也行】。[x、y、z 归于 [-1, 1] 这个规模,便是与 Vertex Position 的值规模共同]
注:上面提到的一切坐标规模是指有用烘托规模,也便是说你假如提供的纹路坐标超出了这个规模也没有问题,只不过超出的部分就不烘托了;
极点数据表明如下:
- 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;
}
烘托代码:
步长和纹路坐标做相应的调整即可,就不贴了
到此正方体贴图工作就完成了。详细请查看源码
可是从学习的博客半纸渊–根底纹路,看到他能完成下图像魔方的作用,已然都做到多面贴图了,所以也想试试看。
经过查看他的源码,这种完成办法也是: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)
}
这并不是咱们想要的作用,并且连方格都没有显现出来,尽管中心看似有分割线。可是离作用图还是天差地别的。怎么回事呢?对比了一下发现在过滤办法不同:GL_LINEAR 和 GL_NEAREST
摘抄自:mChenys — 六、OpenGL ES纹路的运用 当纹路巨细要被扩展或许缩小的时分,咱们需要运用纹路过滤清晰说明会产生什么,当咱们在烘托表面上制作一个纹路时,那个纹路的纹路元素或许无法精确地映射到OpenGL生成的片段上,有2种状况:缩小或许扩大。 当咱们极力把几个纹路元素挤进一个片段时,缩小就会产生了,当把一个纹路元素扩展到许多片段时,扩大就产生了。 针对每一种状况,咱们都能够配置OpenGL运用一个纹路过滤器,我会运用下面的图像阐述每一种过滤形式:
- GL_NEAREST(也叫附近过滤,Nearest Neighbor Filtering)是OpenGL默许的纹路过滤办法。当设置为GL_NEAREST的时分,OpenGL会挑选中心点最接近纹路坐标的那个像素。下图中你能够看到四个像素,加号代表纹路坐标。左上角那个纹路像素的中心距离纹路坐标最近,所以它会被挑选为样本色彩:
这种办法为每个片段挑选最近的纹路元素,当扩大纹路时它的锯齿作用看起来适当明显,每个纹路单元都清楚地显现为一个小方块。
当咱们缩小纹路时,由于没有满足的片段来制作一切的纹路单元,许多细节将会丢失。
- GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹路坐标附近的纹路像素,计算出一个插值,近似出这些纹路像素之间的色彩。一个纹路像素的中心距离纹路坐标越近,那么这个纹路像素的色彩对最终的样本色彩的奉献越大。下图中你能够看到返回的色彩是附近像素的混合色:
线性过滤运用双线插值滑润像素之间的过渡,而不是每个片段运用最近的纹路元素,OpenGL会运用四个邻接的纹路元素,并在他们之间用一个线性差值算法做差值,这个算法与前面介绍滑润着色的算法相同,之所以叫它双线性,是由于它是沿两个维度差值的,它会比近邻过滤要滑润许多,但还是会有一些锯齿显现出来,由于咱们把这个纹路扩展得太多了,可是锯齿没有最近邻过滤那么明显。
PS:纹路扩大时运用线性过滤(GL_LINEAR),缩小时用附近过滤(GL_NEAREST)
然后咱们修正过滤办法为:附近过滤(GL_NEAREST) 作用如下:
这儿看到已经差不多了和他的相同了。可是总觉得怪怪的。便是每一个面都会有一块是黑色的,而对照的却不是这样的。然后再去仔细看了看。后面发现本来还是在这个办法上呈现了问题:
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
运转成果: