着色器

现在可以修改着色器从纹理中采样颜色数据。我们首先需要修改顶点着色器将纹理坐标传递给片段着色器:

layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

fragTexCoord的值会被插值后传递给片段着色器。我们可以通过将纹理坐标输出为片段颜色来形象地认知这一插值过程:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = vec4(fragTexCoord, 0.0, 1.0);
}

重新编译我们的着色器代码,然后编译运行应用程序,将会看到下面的画面:

image

我们这里使用绿色通道来表示x坐标,使用红色通道来表示y坐标。通过上图的黑色和黄色角落,我们可以确定矩形左上角顶点的纹理坐标为(0,0),右下角顶点的纹理坐标为(1,1)。这种通过将变量值作为片段颜色输出的方法是一个很有用的调试着色器的手段。

在GLSL中,使用uniform变量来表示组合图像采样器描述符。为了在片段着色器中使用描述符,我们需要添加下面的代码:

layout(binding = 1) uniform sampler2D texSampler;

对于一维图像和二维图像需要使用对应的sampler1D和sampler3D变量类型来绑定描述符。

void main() {
    outColor = texture(texSampler, fragTexCoord);
}

在GLSL纹理采样需要使用GLSL内建的texture函数。texture函数使用一个采样器变量和一个纹理坐标作为参数对纹理进行采样。采样器会自动按照我们设定的过滤和变换方式对纹理进行采样。现在编译运行程序,可以看到下面的画面:

image

void main() {
    outColor = texture(texSampler, fragTexCoord * 2.0);
}

读者可以尝试通过将纹理坐标设置为大于1.0的值来观察寻址模式对纹理采样的影响。下图是使用VK_SAMPLER_ADDRESS_MODE_REPEAT寻址模式产生的效果:

image

我们还可以同时使用顶点颜色和纹理颜色来产生最终的片段颜色:

void main() {
    outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0);
}

下图是这样做后产生的效果:

image

现在,我们已经明白了如何在着色器中访问图像数据。通过这项技术,我们可以实现许多复杂的效果,比如后期处理,延迟着色等等。

本章节代码:

C++:

https://vulkan-tutorial.com/code/25_texture_mapping.cpp

Vertex Shader:

https://vulkan-tutorial.com/code/25_shader_textures.vert

Fragment Shader:

https://vulkan-tutorial.com/code/25_shader_textures.frag