采样器:
在着色器中是可以直接访问图像数据,但当图像被作为纹理时,我们通常不这样做。通常我们使用采样器来访问纹理数据,采样器可以自动地对纹理数据进行过滤和变换处理.
采样器进行的过滤操作可以很好地处理纹理采样过密的问题。考虑一个被映射到一个几何图元上的纹理,每个纹素占据了多个片段。如果我们直接采样与片段最近的纹素作为片段颜色,可能就会得到下面第一幅图的效果:
如果使用线性插值组合 4 个最近的纹素,我们可以得到上面第二幅图的效果。当然,有时候我们可能会喜欢第一幅图的效果 (比如在编写类似我的世界这种游戏时),当通常来说我们更多的想要第二幅图的效果。采样器可以自动地为我们进行上面这样的纹理过滤。
与之相反的是纹理采样过疏的问题,这种情况发生在多个纹素被映射到一个片段时。这会造成幻影现象,如下面第一幅图:
上面第二幅图,我们可以看到远处的纹理已经变得模糊不清。解决这一问题的方法是使用采样器进行各向异性过滤。
除了上面这些过滤器,采样器还可以进行变换操作。变换操作发生在采样超出纹理图像实际范围的数据时,下面这些图像就是使用采样器的变换操作产生的:
VkImageView textureImageView ;//纹理图像的图像视图对象
VkSampler textureSampler ;//采样器对象
//判断是否支持各向异性过滤
VkPhysicalDeviceFeatures supportedFeatures ;
vkGetPhysicalDeviceFeatures( device , &supportedFeatures );
return indices.isComplete() && extensionsSupported &&
swapChainAdequate && supportedFeatures.samplerAnisotropy;
//指定应用程序使用的设备特性
VkPhysicalDeviceFeatures deviceFeatures = {};
//各向异性过滤实际上是一个非必需的设备特性,这里指定使用
deviceFeatures.samplerAnisotropy = VK_TRUE;
//创建纹理图像的图像视图对象
void createTextureImageView(){
textureImageView = createImageView(textureImage,
VK_FORMAT_R8G8B8A8_UNORM);
}
//创建图像视图对象
VkImageView createImageView(VkImage image , VkFormat format){
//设置图像视图结构体相关信息
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
//viewType 和 format 成员变量用于指定图像数据的解释方式。
//viewType用于指定图像被看作是一维纹理、二维纹理、三维纹理还是立方体贴图
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
/**
components 成员变量用于进行图像颜色通道的映射。
比如,对于单色纹理,我们可以将所有颜色通道映射到红色通道。
我们也可以直接将颜色通道的值映射为常数 0 或 1。在这里,我们只使用默认的映射
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;*/
//subresourceRange用于指定图像的用途和图像的哪一部分可以被访问
//在这里,我们的图像被用作渲染目标,并且没有细分级别,只存在一个图层
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView imageView;
/**
如果读者在编写 VR 一类的应用程序,可能会使用支持多个层次的交换链。
这时,读者应该为每个图像创建多个图像视图,分别用来访问左眼和右眼两个不同的图层
*/
//创建图像视图
if(vkCreateImageView(device,&viewInfo,nullptr,
&imageView) != VK_SUCCESS){
throw std::runtime_error("failed to create texture image view!");
}
return imageView;
//有了图像视图,就可以将图像作为纹理使用,但作为渲染目标,还需要帧缓冲对象
}
//创建采样器对象
void createTextureSampler(){
//我们会在着色器中使用创建的采样器对象采样纹理数据
VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
/**
magFilter 和 minFilter 成员变量用于指定纹理需要放大和缩小时使用
的插值方法。纹理放大会出现采样过密的问题,纹理缩小会出现采样过疏的问题
*/
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
/**
addressModeU、addressModeV 和 addressModeW 用于指定寻址模式。
这里的 U、V、W 对应 X、Y 和 Z 轴。它们的值可以是下面这些:
• VK_SAMPLER_ADDRESS_MODE_REPEAT:
采样超出图像范围时重复纹理
• VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
采样超出图像范围时重复镜像后的纹理
• VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
采样超出图像范围时使用距离最近的边界纹素
• VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
采样超出图像范围时使用镜像后距离最近的边界纹素
• VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
采样超出图像返回时返回设置的边界颜色
在这里,我们的采样不会超出图像范围.
*/
//VK_SAMPLER_ADDRESS_MODE_REPEAT 来实现平铺纹理的效果
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
/**
anisotropyEnable 和 maxAnisotropy 成员变量和各向异性过滤相关。
通常来说,只要性能允许,我们都会开启各向异性过滤。maxAnisotropy
成员变量用于限定计算最终颜色使用的样本个数。maxAnisotropy 成员变
量的值越小,采样的性能表现越好,但采样结果质量较低。目前为止,还
没有图形硬件能够使用超过 16 个样本,同时即使可以使用超过 16 个样本,
采样效果的增强也开始变得微乎其微。
*/
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 16;
/**
borderColor 成员变量用于指定使用 VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
寻址模式时采样超出图像范围时返回的边界颜色。边界颜色并非可以设置为任意颜色。
它可以被设置为浮点或整型格式的黑色、白色或透明色
*/
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
/**
unnormalizedCoordinates 成员变量用于指定采样使用的坐标系统。将
其设置为 VK_TRUE 时,采样使用的坐标范围为 [0, texWidth) 和 [0,
texHeight)。将其设置为 VK_FALSE,采样使用的坐标范围在所有轴都是
[0, 1)。通常使用 VK_FALSE 的情况更常见,这种情况下我们可以使用相
同的纹理坐标采样不同分辨率的纹理
*/
samplerInfo.unnormalizedCoordinates = VK_FALSE;
/**
通过 compareEnable 和 compareOp 成员变量,我们可以将样本和一
个设定的值进行比较,然后将比较结果用于之后的过滤操作。通常我们在
进行阴影贴图时会使用它
*/
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
/**
mipmapMode、mipLodBias、minLod 和 maxLod 成员变量用于设置
分级细化 (mipmap),它可以看作是过滤操作的一种
*/
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
/**
需要注意,采样器对象并不引用特定的 VkImage 对象,它是一个用于
访问纹理数据的接口。我们可以使用它来访问任意不同的图像,不管图像
是一维的、二维的、还是三维的。这和一些旧的图形 API 将纹理图像和过
滤设置绑定在一起进行采样是不同的
*/
if(vkCreateSampler(device,&samplerInfo,nullptr,
&textureSampler) != VK_SUCCESS){
throw std::runtime_error("failed to create texture sampler!");
}
}
//清除采样器对象
vkDestroySampler(device,textureSampler,nullptr);
//清除纹理图像的图像视图对象,
vkDestroyImageView ( device , textureImageView , nullptr ) ;