前言
一般,在三维项目添加纹理的时候,经常会看到有和纹理操作的函数,先看一段片元着色器程序:在片元着色器中
#version 450 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D depthMap;
uniform float near_plane;
uniform float far_plane;
// required when using a perspective projection matrix
float LinearizeDepth(float depth)
{
float z = depth * 2.0f - 1.0f; //Back to NDC
return (2.0f * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}
void main()
{
float depthValue = texture(depthMap, TexCoords).r;
// FragColor = vec4(vec3(LinearizeDepth(depthValue) / near_plane), 1.0f); // perspective
FragColor = vec4(vec3(depthValue), 1.0f); // orthographic
}
.cpp文件代码
//plane VAO VBO
unsigned int planeVBO;
glGenVertexArrays(1, &planeVAO);
glGenBuffers(1, &planeVBO);
glBindVertexArray(planeVAO);
glBindBuffer(GL_ARRAY_BUFFER, planeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), &planeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(0);
// load textures
// -------------
unsigned int woodTexture = loadTexture(FileSystem::getPath("resources/textures/wood.png").c_str());
// configure depth map FBO
// -----------------------
const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
unsigned int depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);
//glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); //为什么不绑定帧缓冲对象
// create depth texture
unsigned int depthMap;
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
/******* 功能:指定一个二维纹理图像,纹理将指定纹理图像的一部分映射到纹理化为活动的每个图形基元上。
/******* 当前片段着色器或顶点着色器使用内置纹理查找函数时,纹理处于活动状态
/******* 参数1:target:指定活动纹理单元的目标纹理。必须是GL_TEXTURE_2D,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_X,GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
/******* GL_TEXTURE_CUBE_MAP_POSITIVE_Z,或GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
/******* 参数2:level:指定详细程度编号。级别0是基本图像级别。级别n是第n个缩略图缩小图像。
/******* 参数3:internalformat指定纹理的内部格式。必须是下列符号常量之一:GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA
/******* 这里我们Base Internal Format:包括:GL_DEPTH_COMPONENT,GL_DEPTH_STENCIL,GL_RED,GL_RG,GL_RGB,GL_RGBA,
/******* 因为我们只关心深度贴图,所以我们把纹理格式定义为:GL_DEPTH_COMPONENT
/******* 参数4:width:深度贴图解析度,指定纹理图像的宽度。所有实现都支持宽度至少为64 texels的2D纹理图像和宽度至少为16 texels的立方体贴图纹理图像。
/******* 参数5:height:指定纹理图像的高度所有实现都支持至少64像素高的2D纹理图像和至少16像素高的立方体贴图纹理图像。
/******* 参数6:border指定边框的宽度。必须为0。
/******* 参数7:format指定纹理数据的格式。必须匹配internalformat。下面的符号值被接受:GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE,和GL_LUMINANCE_ALPHA
/******* 参数8:指定纹理数据的数据类型。下面的符号值被接受:GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,和GL_UNSIGNED_SHORT_5_5_5_1
/******* 参数9:data指定一个指向内存中图像数据的指针
*******/
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
问题
当看到和纹理操作相关的函数,总是不知其意,理解不了,现在详细说明一下:下面这段是引用以为大神的文章“”
严正声明:
作者:psklf 出处: javascript:void(0) 欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任!
Sampler (GLSL)
Sampler通常是在Fragment shader(片元着色器)内定义的,这是一个uniform类型的变量,即处理不同的片元时这个变量是一致不变的。一个sampler和一个texture对应,类型也是对应的,比如sampler2D
的sampler对应的就是GL_TEXTURE_2D
类型的纹理对象。Sampler是个变量,但是它是没有值的,或者說是特殊的一种类型,讨论其数值没有意义,只要明确其同一个texture对应即可。sampler变量在shader内使用的地方就是texture
函数。这是一个lookup 函数(我不知道该如何翻译,我的理解是这是一个查找/查询的函数,以给定的纹理坐标去纹理的数据中查到相应的颜色的信息),得到颜色信息渲在每一个片元上。例如,一个三角形,我们只要传递三个纹理坐标给顶点着色器就行了,接着片元着色器会为每个像素生成纹理坐标的插值,根据纹理坐标就得到了每一个像素的颜色值。
纹理采样的机制
Texture Wrapping
纹理坐标通常的范围是从(0, 0)到(1, 1),如果我们把纹理坐标设置为范围以外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们简单地忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:
- GL_REPEAT:纹理的默认行为。重复纹理图像。
- GL_MIRRORED_REPEAT:和GL_REPEAT一样,除了重复的图片是镜像放置的。
- GL_CLAMP_TO_EDGE:纹理坐标会在0到1之间。超出的部分会重复纹理坐标的边缘,就是边缘被拉伸。
- GL_CLAMP_TO_BORDER:超出的部分是用户指定的边缘的颜色。
在生成纹理对象的时候,需要用 glTexParameter()
设置一系列参数,设置的就是这个参数以及下面的 Texture Filtering 的参数。
Texture Filtering
magnification/minification
组成纹理的图片数据和其要贴上去的形状的大小往往是不一样的。两种情况:
- magnification:纹理图片小,贴图区域大,需要放大纹理
- minification:反过来,纹理图片大,贴图区域小,缩小纹理显示出来
在做放大和缩小的操作的时候的具体的策略如下:
- GL_NEAREST:直接选择最临近的像素的颜色,magnification(放大)时:由于多个片元会在同一个纹理像素上面取值,故最终得到的图片颗粒度很大,会有锯齿。
- GL_LINEAR:根据临近四个的像素点的颜色值,做线性的插值计算,得到最终的颜色。magnification(放大)时:不会产生锯齿,显示更加平滑。
在minification(缩小)时,上面的两种方法其实都不理想,无论如何都会丢失很多图片的细节,OpenGL 用Mipmap来解决这个问题。
Mipmap
它就是一系列纹理,每个后面的一个纹理是前一个的二分之一,这一系列的纹理是OpenGL生成的,生成时进行了图像质量的优化,使其拥有更多的细节。这一系列的纹理是提前生成的,程序运行时只需要从中挑出合适大小的纹理应用即可,而不是运行时进行图像大小的处理,效率上会有提高。
OpenGL渲染的时候,两个不同级别的mipmap之间会产生不真实感的生硬的边界。就像普通的纹理过滤一样,也可以在两个不同mipmap级别之间使用NEAREST和LINEAR过滤。指定不同mipmap级别之间的过滤方式可以使用下面四种选项代替原来的过滤方式:
- GL_NEAREST_MIPMAP_NEAREST:接收最近的mipmap来匹配像素大小,并使用最临近插值进行纹理采样。
- GL_LINEAR_MIPMAP_NEAREST:接收最近的mipmap级别,并使用线性插值采样。
- GL_NEAREST_MIPMAP_LINEAR:在两个mipmap之间进行线性插值,通过最邻近插值采样。
- GL_LINEAR_MIPMAP_LINEAR:在两个相邻的mipmap进行线性插值,并通过线性插值进行采样。
总结一下:magnification和minification的时候都可以设置NEAREST和LINEAR两种方式;minification的时候还可以设置mipmap的方式,该方法效果更好。关于具体的算法的实现,可以参考《OpenGL ES specification》的8.13-8.14内容。
Sampler object
一个texture对象包括了两部分的属性,一部分是具体的图片信息,另一部分是纹理采样的设置,即上文提到的不同的方式。通常生成纹理的时候是将这两部分一起设置好的,但是后面这部分的内容可以单独拿出来,封装成为一个对象,就是 sampler object
.
使用 GenSamplers()
函数创建一个新的sampler对象,然后用 BindSampler()
做绑定操作,将sampler和texture对象绑定起来,然后调用 glSamplerParameterf()
这一类的函数来设置sampler的具体的参数。
texture object, sampler object, program object 的关系如图:
另外,看一下texture在openGL整个渲染流程中的位置
官方文档地址
原文以及翻译
Name
texture — retrieves texels from a texture
取出二维纹理中该纹理坐标点上的纹理像素值
Declaration
| gsampler2D sampler, |
vec2 P, | |
[float bias] |
| gsampler3D sampler, |
vec3 P, | |
[float bias] |
| gsamplerCube sampler, |
vec3 P, | |
[float bias] |
| sampler2DShadow sampler, |
vec3 P, | |
[float bias] |
| samplerCubeShadow sampler, |
vec4 P, | |
[float bias] |
| gsampler2DArray sampler, |
vec3 P, | |
[float bias] |
| sampler2DArrayShadow sampler, |
vec4 P |
| gsamplerCubeArray sampler, |
vec4 P, | |
[float bias] |
| samplerCubeArrayShadow sampler, |
vec4 P, | |
float compare |
Parameters
sampler
Specifies the sampler to which the texture from which texels will be retrieved is bound.
指定纹理绑定到的采样器
P
Specifies the texture coordinates at which texture will be sampled.
指定纹理采样的纹理坐标
bias
Specifies an optional bias to be applied during level-of-detail computation.
指定在详细级别计算期间应用的可选偏差。 (可省略)
compare
When present, specifies the reference for shadow comparisons.
当存在时,指定阴影比较的参考
Description
texture
samples texels from the texture bound to sampler
at texture coordinate P
. An optional bias, specified in bias
For shadow forms, when compare
is present, it is used as DsubDsub and the array layer is specified in P.w
. When compare
is not present, the last component of P
is used as DsubDsub and the array layer is specified in the second to last component of P
.
For non-shadow variants, the array layer comes from the last component of P
.
在纹理坐标p处,从纹理绑定到采样器的纹理纹理素。一个可选的偏差,在偏差中指定,被包含在详细级别计算中,用于选择从其中取样的mipmap(s)。
对于阴影形式,当compare存在时,它被用作DsubDsub,数组层在P.w中指定。当compare不存在时,P的最后一个组件被用作DsubDsub,数组层在P的倒数第二个组件中指定。
对于非阴影变量,阵列层来自于P的最后一个分量。
Version Support
OpenGL ES Shading Language Version | ||||
Function Name | 1.00 | 3.00 | 3.10 | 3.20 |
texture | - | ✔ | ✔ | ✔ |
texture (gSamplerCubeArray, samplerCubeArrayShadow) | - | - | - | ✔ |
See Also
texelFetch, texelFetchOffset, textureGrad, textureGradOffset, textureLod, textureLodOffset, textureOffset, textureProj, textureProjGrad, textureProjGradOffset, textureProjLod, textureProjLodOffset, textureProjOffset, textureSize
Copyright
Copyright © 2011-2015 Khronos Group. This material may be distributed subject to the terms and conditions set forth in the Open Publication License, v 1.0, 8 June 1999. http://opencontent.org/openpub/.