Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 384df88

Browse files
committed
Fix formatting
Make formatting consistent with other chapters
1 parent 3f92f53 commit 384df88

1 file changed

Lines changed: 135 additions & 136 deletions

File tree

09_Generating_Mipmaps.md

Lines changed: 135 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ In Vulkan, each of the mip images is stored in different *mip levels* of a `VkIm
1212
The number of mip levels is specified when the `VkImage` is created. Up until now, we have always set this value to one. We need to calculate the number of mip levels from the dimensions of the image. First, add a class member to store this number:
1313

1414
```c++
15-
...
16-
uint32_t mipLevels;
17-
VkImage textureImage;
18-
...
15+
...
16+
uint32_t mipLevels;
17+
VkImage textureImage;
18+
...
1919
```
2020

2121
The value for `mipLevels` can be found once we've loaded the texture in `createTextureImage`:
2222

2323
```c++
24-
int texWidth, texHeight, texChannels;
25-
stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
26-
...
27-
mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
24+
int texWidth, texHeight, texChannels;
25+
stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
26+
...
27+
mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
2828

2929
```
3030

@@ -33,32 +33,32 @@ This calculates the number of levels in the mip chain. The `max` function select
3333
To use this value, we need to change the `createImage` and `createImageView` functions to allow us to specify the number of mip levels. Add a `mipLevels` parameter to the functions:
3434

3535
```c++
36-
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
37-
...
38-
imageInfo.mipLevels = mipLevels;
39-
...
40-
}
36+
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
37+
...
38+
imageInfo.mipLevels = mipLevels;
39+
...
40+
}
4141
```
4242
```c++
43-
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) {
44-
...
45-
viewInfo.subresourceRange.levelCount = mipLevels;
46-
...
43+
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) {
44+
...
45+
viewInfo.subresourceRange.levelCount = mipLevels;
46+
...
4747
```
4848

4949
Update all calls to these functions to use the right values:
5050

5151
```c++
52-
createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
53-
...
54-
createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
52+
createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
53+
...
54+
createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
5555
```
5656
```c++
57-
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
58-
...
59-
depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
60-
...
61-
textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
57+
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
58+
...
59+
depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
60+
...
61+
textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
6262
```
6363

6464

@@ -70,147 +70,146 @@ Our texture image now has multiple mip levels, but the staging buffer can only b
7070
Like other image operations, `vkCmdBlitImage` depends on the layout of the image it operates on. For optimal performance, the source image should be in `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL` and the destination image should be in `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL` while blitting data. Vulkan allows us to transition each mip level of an image independently. `transitionImageLayout` only performs layout transitions on the entire image, so we'll need to write a few more pipeline barrier commands. First, remove the existing transition to `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` in `createTextureImage`:
7171

7272
```c++
73-
...
74-
createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
75-
76-
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
77-
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
78-
//transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
79-
...
73+
...
74+
createImage(texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
75+
76+
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
77+
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
78+
//transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
79+
...
8080
```
8181
This will leave each level of the texture image in `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL`.
8282
8383
We're now going to write the function generates the mipmaps:
8484
8585
```c++
86-
void generateMipmaps(VkImage image, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {
87-
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
88-
89-
VkImageMemoryBarrier barrier = {};
90-
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
91-
barrier.image = image;
92-
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
93-
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
94-
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
95-
barrier.subresourceRange.baseArrayLayer = 0;
96-
barrier.subresourceRange.layerCount = 1;
97-
barrier.subresourceRange.levelCount = 1;
98-
99-
endSingleTimeCommands(commandBuffer);
100-
}
86+
void generateMipmaps(VkImage image, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {
87+
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
88+
89+
VkImageMemoryBarrier barrier = {};
90+
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
91+
barrier.image = image;
92+
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
93+
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
94+
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
95+
barrier.subresourceRange.baseArrayLayer = 0;
96+
barrier.subresourceRange.layerCount = 1;
97+
barrier.subresourceRange.levelCount = 1;
98+
99+
endSingleTimeCommands(commandBuffer);
100+
}
101101
```
102102

103103
We're going to make several transitions, so we'll reuse this `VkImageMemoryBarrier`.
104104

105105
```c++
106-
int32_t mipWidth = texWidth;
107-
int32_t mipHeight = texHeight;
106+
int32_t mipWidth = texWidth;
107+
int32_t mipHeight = texHeight;
108108

109-
for (uint32_t i = 1; i < mipLevels; i++) {
110-
111-
}
109+
for (uint32_t i = 1; i < mipLevels; i++) {
110+
111+
}
112112
```
113113

114114
This loop will record each of the `VkCmdBlitImage` commands. Note that the loop variable starts at 1, not 0.
115115

116116
```c++
117-
barrier.subresourceRange.baseMipLevel = i - 1;
118-
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
119-
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
120-
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
121-
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
122-
123-
vkCmdPipelineBarrier(commandBuffer,
124-
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
125-
0, nullptr,
126-
0, nullptr,
127-
1, &barrier);
117+
barrier.subresourceRange.baseMipLevel = i - 1;
118+
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
119+
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
120+
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
121+
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
122+
123+
vkCmdPipelineBarrier(commandBuffer,
124+
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
125+
0, nullptr,
126+
0, nullptr,
127+
1, &barrier);
128128
```
129129
130130
First, we transition level `i - 1` to `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`.
131131
132132
```c++
133-
VkImageBlit blit = {};
134-
blit.srcOffsets[0] = { 0, 0, 0 };
135-
blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
136-
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
137-
blit.srcSubresource.mipLevel = i - 1;
138-
blit.srcSubresource.baseArrayLayer = 0;
139-
blit.srcSubresource.layerCount = 1;
140-
blit.dstOffsets[0] = { 0, 0, 0 };
141-
blit.dstOffsets[1] = { mipWidth / 2, mipHeight / 2, 1 };
142-
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
143-
blit.dstSubresource.mipLevel = i;
144-
blit.dstSubresource.baseArrayLayer = 0;
145-
blit.dstSubresource.layerCount = 1;
133+
VkImageBlit blit = {};
134+
blit.srcOffsets[0] = { 0, 0, 0 };
135+
blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
136+
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
137+
blit.srcSubresource.mipLevel = i - 1;
138+
blit.srcSubresource.baseArrayLayer = 0;
139+
blit.srcSubresource.layerCount = 1;
140+
blit.dstOffsets[0] = { 0, 0, 0 };
141+
blit.dstOffsets[1] = { mipWidth / 2, mipHeight / 2, 1 };
142+
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
143+
blit.dstSubresource.mipLevel = i;
144+
blit.dstSubresource.baseArrayLayer = 0;
145+
blit.dstSubresource.layerCount = 1;
146146
```
147147

148148
Next, we specify the regions that will be used in the blit operation. The source mip level is `i - 1` and the destination mip level is `i`. The two elements of the `srcOffsets` array determine the 3D region that data will be blitted from. `dstOffsets` determines the region that data will be blitted to. The X and Y dimensions of the `dstOffsets[1]` are divided by two since each mip level is half the size of the previous level. The Z dimension of `srcOffsets[1]` and `dstOffsets[1]` must be 1, since a 2D image has a depth of 1.
149149

150150
```c++
151-
vkCmdBlitImage(commandBuffer,
152-
image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
153-
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
154-
1, &blit,
155-
VK_FILTER_LINEAR);
151+
vkCmdBlitImage(commandBuffer,
152+
image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
153+
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
154+
1, &blit,
155+
VK_FILTER_LINEAR);
156156
```
157157
158158
Now, we record the blit command. Note that `textureImage` is used for both the `srcImage` and `dstImage` parameter. This is because we're blitting between different levels of the same image. The source mip level was just transitioned to `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL` and the destination level is still in `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL` from `createTextureImage`.
159159
160160
The last parameter allows us to specify a `VkFilter` to use in the blit. We have the same filtering options here that we had when making the `VkSampler`. We use the `VK_FILTER_LINEAR` to enable filtering.
161161
162162
```c++
163-
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
164-
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
165-
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
166-
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
167-
168-
vkCmdPipelineBarrier(commandBuffer,
169-
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
170-
0, nullptr,
171-
0, nullptr,
172-
1, &barrier);
163+
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
164+
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
165+
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
166+
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
167+
168+
vkCmdPipelineBarrier(commandBuffer,
169+
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
170+
0, nullptr,
171+
0, nullptr,
172+
1, &barrier);
173173
```
174174

175175
This barrier transitions mip level `i - 1` to `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL`.
176176

177177
```c++
178-
...
179-
if (mipWidth > 1) mipWidth /= 2;
180-
if (mipHeight > 1) mipHeight /= 2;
181-
}
178+
...
179+
if (mipWidth > 1) mipWidth /= 2;
180+
if (mipHeight > 1) mipHeight /= 2;
181+
}
182182
```
183183

184184
At the end of the loop, we divide the current mip dimensions by two. We check each dimension before the division to ensure that dimension never becomes 0. This handles cases where the image is not square, since one of the mip dimensions would reach 1 before the other dimension. When this happens, that dimension should remain 1 for all remaining levels.
185185

186186
```c++
187+
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
188+
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
189+
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
190+
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
191+
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
187192

188-
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
189-
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
190-
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
191-
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
192-
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
193-
194-
vkCmdPipelineBarrier(commandBuffer,
195-
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
196-
0, nullptr,
197-
0, nullptr,
198-
1, &barrier);
193+
vkCmdPipelineBarrier(commandBuffer,
194+
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
195+
0, nullptr,
196+
0, nullptr,
197+
1, &barrier);
199198

200-
endSingleTimeCommands(commandBuffer);
201-
}
199+
endSingleTimeCommands(commandBuffer);
200+
}
202201
```
203202
204203
Before we end the command buffer, we insert one more pipeline barrier. This barrier transitions the last mip level from `VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL` to `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL`, since that wasn't handled by the loop.
205204
206205
Finally, add the call to `generateMipmaps` in `createTextureImage`:
207206
208207
```c++
209-
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
210-
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
211-
//transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
212-
...
213-
generateMipmaps(textureImage, texWidth, texHeight, mipLevels);
208+
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
209+
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
210+
//transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
211+
...
212+
generateMipmaps(textureImage, texWidth, texHeight, mipLevels);
214213
```
215214

216215
Our texture image's mipmaps are now completely filled.
@@ -220,43 +219,43 @@ Our texture image's mipmaps are now completely filled.
220219
While the `VkImage` holds the mipmap data, `VkSampler` controls how that data is read while rendering. Vulkan allows us to specify `minLod`, `maxLod`, `mipLodBias`, and `mipmapMode` ("Lod" means "Level of Detail"). When a texture is sampled, the sampler selects a mip level according to the following pseudocode:
221220

222221
```c++
223-
lod = getLodLevelFromScreenSize(); //smaller when the object is close, may be negative
224-
lod = clamp(lod + mipLodBias, minLod, maxLod);
225-
226-
level = clamp(floor(lod), 0, texture.mipLevels - 1); //clamped to the number of mip levels in the texture
227-
228-
if (mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST) {
229-
color = sample(level);
230-
} else {
231-
color = blend(sample(level), sample(level + 1));
232-
}
222+
lod = getLodLevelFromScreenSize(); //smaller when the object is close, may be negative
223+
lod = clamp(lod + mipLodBias, minLod, maxLod);
224+
225+
level = clamp(floor(lod), 0, texture.mipLevels - 1); //clamped to the number of mip levels in the texture
226+
227+
if (mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST) {
228+
color = sample(level);
229+
} else {
230+
color = blend(sample(level), sample(level + 1));
231+
}
233232
```
234233

235234
If `samplerInfo.mipmapMode` is `VK_SAMPLER_MIPMAP_MODE_NEAREST`, `lod` selects the mip level to sample from. If the mipmap mode is `VK_SAMPLER_MIPMAP_MODE_LINEAR`, `lod` is used to select two mip levels to be sampled. Those levels are sampled and the results are linearly blended.
236235

237236
The sample operation is also affected by `lod`:
238237

239238
```c++
240-
if (lod <= 0) {
241-
color = readTexture(uv, magFilter);
242-
} else {
243-
color = readTexture(uv, minFilter);
244-
}
239+
if (lod <= 0) {
240+
color = readTexture(uv, magFilter);
241+
} else {
242+
color = readTexture(uv, minFilter);
243+
}
245244
```
246245

247246
If the object is close to the camera, `magFilter` is used as the filter. If the object is further from the camera, `minFilter` is used. Normally, `lod` is non-negative, and is only 0 when close the camera. `mipLodBias` lets us force Vulkan to use lower `lod` and `level` than it would normally use.
248247

249248
To see the results of this chapter, we need to choose values for our `textureSampler`. We've already set the `minFilter` and `magFilter` to use `VK_FILTER_LINEAR`. We just need to choose values for `minLod`, `maxLod`, `mipLodBias`, and `mipmapMode`.
250249

251250
```c++
252-
void createTextureSampler() {
253-
...
254-
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
255-
samplerInfo.minLod = 0; // Optional
256-
samplerInfo.maxLod = static_cast<float>(mipLevels);
257-
samplerInfo.mipLodBias = 0; // Optional
258-
...
259-
}
251+
void createTextureSampler() {
252+
...
253+
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
254+
samplerInfo.minLod = 0; // Optional
255+
samplerInfo.maxLod = static_cast<float>(mipLevels);
256+
samplerInfo.mipLodBias = 0; // Optional
257+
...
258+
}
260259
```
261260

262261
To allow the full range of mip levels to be used, we set `minLod` to 0, and `maxLod` to the number of mip levels. We have no reason to change the `lod` value , so we set `mipLodBias` to 0.

0 commit comments

Comments
 (0)