我们使用纹理为图形增加更多的细节。
纹理坐标在x和y轴上,范围为0到1之间。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标始于(0, 0),也就是纹理图片的左下角,终于(1, 1),即纹理图片的右上角。
(相关资料图)
创建纹理和之前创建OpenGL对象的方式一样。
GL_TEXTURE_2D是我们使用的纹理类型。
创建纹理后,我们需要设置纹理环绕方式。纹理环绕方式用来控制纹理在物体表面上的重复、拉伸或镜像效果。以下是常见的纹理环绕方式:
GL_REPEAT(默认):纹理在物体表面上重复平铺。
GL_MIRRORED_REPEAT:和GL_REPEAT一样,但在每次重复时,纹理会进行镜像翻转。
GL_CLAMP_TO_EDGE:纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER:超出范围的纹理坐标会使用指定的边界颜色来采样。
我们一般使用函数glTexParameteri()设置纹理环绕方式。
第一个参数指定了纹理类型,譬如我们使用的是2D纹理,为GL_TEXTURE_2D。第二个参数为设置的选项。第三个参数是纹理环绕方式。
下面设置了水平方向和垂直方向的环绕方式:
GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T分别代表纹理坐标的水平和垂直方向。
我们使用stb_image库来加载图像。
函数stbi_load()可以将图片加载到内存中:
filename是要加载的图像文件的文件名。
width是一个指向整数的指针,用于存储图像的宽度。
height用于存储图像的高度。
channels用于存储图像的通道数。
desired_channels是期望的输出通道数,通常可以设置为0来保持原始图像通道数。
注意,由于OpenGL要求y轴0坐标在图片的底部,但是通常图片的y轴的0坐标通常在顶部。因此我们加载的纹理是上下颠倒的。我们需要在加载纹理之前调用翻转y轴的函数:
现在我们使用之前创建的纹理对象和加载进来的图片生成一个纹理。
调用函数glTexImage2D()。
target: 纹理目标,通常为GL_TEXTURE_2D。
level: 纹理级别,通常为0,表示基本级别。
internalFormat: 指定纹理的内部格式,比如GL_RGB。
width: 纹理的宽度。
height: 纹理的高度。
border: 边框的宽度,通常为0。
format: 传入数据的格式,比如GL_RGB。
type: 传入数据的数据类型。
data: 指向图像数据的指针。
接下来我们还需要调用glGenerateMipmap(GL_TEXTURE_2D)。这个函数的作用我们暂且忽略,之后会提及。
生成纹理后,还应及时释放图像的内存。
下面是生成一个纹理的基本流程:
首先更新顶点数据,增加纹理坐标。
接着配置顶点属性。之后我们要更新着色器代码。
顶点着色器:我们把纹理坐标发送到顶点着色器中,再从顶点着色器发送到片段着色器。
片段着色器:现在片段着色器最终输出的颜色应是对纹理采样得到的颜色。
函数texture()在GLSL中用于从纹理中采样颜色值。
sampler是一个二维纹理采样器类型,用于指定从哪个纹理单元中进行采样。可以将纹理单元视为存储纹理图像的位置。coordinates是一个二维向量,表示从纹理中采样的坐标。texture()会根据指定的纹理采样器和坐标,在纹理图像上找到对应位置的颜色值,并返回一个vec4类型的颜色值。所以我们要在片段着色器中声明一个采样器变量sampler。
虽然sampler是一个uniform全局变量,但我们并不需要对其赋值。原因之后会提及。
到此我们已经可以成功把一个纹理应用在图形上了:
在片段着色器中,我们还可以把纹理和颜色进行混合,只需把二者相乘:
这是混合后的效果:
广告
X 关闭
广告
X 关闭