# 前言

Metal系列教程的代码地址； OpenGL ES系列教程在这里

### 正文

#### 具体步骤

##### 1、绘制一个正方体

```        // 顶点坐标，                      顶点颜色，                  纹理坐标，
// 正方体上面的四个点
{{-0.5f, 0.5f, 0.5f, 1.0f},      {1.0f, 0.0f, 0.0f},       {0.0f, 1.0f}},//左上 0
{{0.5f, 0.5f, 0.5f, 1.0f},       {0.0f, 1.0f, 0.0f},       {1.0f, 1.0f}},//右上 1
{{-0.5f, -0.5f, 0.5f, 1.0f},     {0.0f, 0.0f, 1.0f},       {0.0f, 0.0f}},//左下 2
{{0.5f, -0.5f, 0.5f, 1.0f},      {1.0f, 1.0f, 1.0f},       {1.0f, 0.0f}},//右下 3

// 正方体下面的四个点
{{-0.5f, 0.5f, -0.5f, 1.0f},      {1.0f, 0.0f, 0.0f},       {0.0f, 1.0f}},//左上 4
{{0.5f, 0.5f, -0.5f, 1.0f},       {0.0f, 1.0f, 0.0f},       {1.0f, 1.0f}},//右上 5
{{-0.5f, -0.5f, -0.5f, 1.0f},     {0.0f, 0.0f, 1.0f},       {0.0f, 0.0f}},//左下 6
{{0.5f, -0.5f, -0.5f, 1.0f},      {1.0f, 1.0f, 1.0f},       {1.0f, 0.0f}},//右下 7```

##### 3、纹理转换

```        // 顶点坐标，                      顶点颜色，                  纹理坐标，

// 上面
{{-6.0f, 6.0f, 6.0f, 1.0f},      {1.0f, 0.0f, 0.0f},       {0.0f, 2.0f/6}},//左上 0
{{-6.0f, -6.0f, 6.0f, 1.0f},     {0.0f, 0.0f, 1.0f},       {0.0f, 3.0f/6}},//左下 2
{{6.0f, -6.0f, 6.0f, 1.0f},      {1.0f, 1.0f, 1.0f},       {1.0f, 3.0f/6}},//右下 3

{{-6.0f, 6.0f, 6.0f, 1.0f},      {1.0f, 0.0f, 0.0f},       {0.0f, 2.0f/6}},//左上 0
{{6.0f, 6.0f, 6.0f, 1.0f},       {0.0f, 1.0f, 0.0f},       {1.0f, 2.0f/6}},//右上 1
{{6.0f, -6.0f, 6.0f, 1.0f},      {1.0f, 1.0f, 1.0f},       {1.0f, 3.0f/6}},//右下 3

// 下面
{{-6.0f, 6.0f, -6.0f, 1.0f},     {1.0f, 0.0f, 0.0f},       {0.0f, 4.0f/6}},//左上 4
{{6.0f, 6.0f, -6.0f, 1.0f},      {0.0f, 1.0f, 0.0f},       {1.0f, 4.0f/6}},//右上 5
{{6.0f, -6.0f, -6.0f, 1.0f},     {1.0f, 1.0f, 1.0f},       {1.0f, 3.0f/6}},//右下 7

{{-6.0f, 6.0f, -6.0f, 1.0f},     {1.0f, 0.0f, 0.0f},       {0.0f, 4.0f/6}},//左上 4
{{-6.0f, -6.0f, -6.0f, 1.0f},    {0.0f, 0.0f, 1.0f},       {0.0f, 3.0f/6}},//左下 6
{{6.0f, -6.0f, -6.0f, 1.0f},     {1.0f, 1.0f, 1.0f},       {1.0f, 3.0f/6}},//右下 7

// 左面
{{-6.0f, 6.0f, 6.0f, 1.0f},      {1.0f, 0.0f, 0.0f},       {0.0f, 1.0f/6}},//左上 0
{{-6.0f, -6.0f, 6.0f, 1.0f},     {0.0f, 0.0f, 1.0f},       {1.0f, 1.0f/6}},//左下 2
{{-6.0f, 6.0f, -6.0f, 1.0f},     {1.0f, 0.0f, 0.0f},       {0.0f, 2.0f/6}},//左上 4

{{-6.0f, -6.0f, 6.0f, 1.0f},     {0.0f, 0.0f, 1.0f},       {1.0f, 1.0f/6}},//左下 2
{{-6.0f, 6.0f, -6.0f, 1.0f},     {1.0f, 0.0f, 0.0f},       {0.0f, 2.0f/6}},//左上 4
{{-6.0f, -6.0f, -6.0f, 1.0f},    {0.0f, 0.0f, 1.0f},       {1.0f, 2.0f/6}},//左下 6

// 右面
{{6.0f, 6.0f, 6.0f, 1.0f},       {0.0f, 1.0f, 0.0f},       {1.0f, 0.0f/6}},//右上 1
{{6.0f, -6.0f, 6.0f, 1.0f},      {1.0f, 1.0f, 1.0f},       {0.0f, 0.0f/6}},//右下 3
{{6.0f, 6.0f, -6.0f, 1.0f},      {0.0f, 1.0f, 0.0f},       {1.0f, 1.0f/6}},//右上 5

{{6.0f, -6.0f, 6.0f, 1.0f},      {1.0f, 1.0f, 1.0f},       {0.0f, 0.0f/6}},//右下 3
{{6.0f, 6.0f, -6.0f, 1.0f},      {0.0f, 1.0f, 0.0f},       {1.0f, 1.0f/6}},//右上 5
{{6.0f, -6.0f, -6.0f, 1.0f},     {1.0f, 1.0f, 1.0f},       {0.0f, 1.0f/6}},//右下 7

// 前面
{{-6.0f, -6.0f, 6.0f, 1.0f},     {0.0f, 0.0f, 1.0f},       {0.0f, 4.0f/6}},//左下 2
{{6.0f, -6.0f, 6.0f, 1.0f},      {1.0f, 1.0f, 1.0f},       {1.0f, 4.0f/6}},//右下 3
{{6.0f, -6.0f, -6.0f, 1.0f},     {1.0f, 1.0f, 1.0f},       {1.0f, 5.0f/6}},//右下 7

{{-6.0f, -6.0f, 6.0f, 1.0f},     {0.0f, 0.0f, 1.0f},       {0.0f, 4.0f/6}},//左下 2
{{-6.0f, -6.0f, -6.0f, 1.0f},    {0.0f, 0.0f, 1.0f},       {0.0f, 5.0f/6}},//左下 6
{{6.0f, -6.0f, -6.0f, 1.0f},     {1.0f, 1.0f, 1.0f},       {1.0f, 5.0f/6}},//右下 7

// 后面
{{-6.0f, 6.0f, 6.0f, 1.0f},      {1.0f, 0.0f, 0.0f},       {1.0f, 5.0f/6}},//左上 0
{{6.0f, 6.0f, 6.0f, 1.0f},       {0.0f, 1.0f, 0.0f},       {0.0f, 5.0f/6}},//右上 1
{{6.0f, 6.0f, -6.0f, 1.0f},      {0.0f, 1.0f, 0.0f},       {0.0f, 6.0f/6}},//右上 5

{{-6.0f, 6.0f, 6.0f, 1.0f},      {1.0f, 0.0f, 0.0f},       {1.0f, 5.0f/6}},//左上 0
{{-6.0f, 6.0f, -6.0f, 1.0f},     {1.0f, 0.0f, 0.0f},       {1.0f, 6.0f/6}},//左上 4
{{6.0f, 6.0f, -6.0f, 1.0f},      {0.0f, 1.0f, 0.0f},       {0.0f, 6.0f/6}},//右上 5```

#### 4、调整投影矩阵和模型变换矩阵

```    // 调整眼睛的位置
self.eyePosition = GLKVector3Make(2.0f * sinf(angle),
2.0f * cosf(angle),
0.0f);

// 调整观察的位置
self.lookAtPosition = GLKVector3Make(2.0f * sinf(angleLook),
2.0f * cosf(angleLook),
2.0f);

GLKMatrix4 modelViewMatrix = GLKMatrix4MakeLookAt(
self.eyePosition.x,
self.eyePosition.y,
self.eyePosition.z,
self.lookAtPosition.x,
self.lookAtPosition.y,
self.lookAtPosition.z,
self.upVector.x,
self.upVector.y,
self.upVector.z); // 模型变换矩阵```

```vertex RasterizerData
vertexShader(uint vertexID [[ vertex_id ]], // 顶点索引
constant LYVertex *vertexArray [[ buffer(LYVertexInputIndexVertices) ]], // 顶点数据
constant LYMatrix *matrix [[ buffer(LYVertexInputIndexMatrix) ]]) { // 变换矩阵
RasterizerData out; // 输出数据
out.clipSpacePosition = matrix->projectionMatrix * matrix->modelViewMatrix * vertexArray[vertexID].position; // 变换处理
out.textureCoordinate = vertexArray[vertexID].textureCoordinate; // 纹理坐标
out.pixelColor = vertexArray[vertexID].color; // 顶点颜色，调试用
return out;
}

fragment float4
texture2d<half> textureColor [[ texture(LYFragmentInputIndexTexture) ]])
{
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear); // 采样器
half4 colorTex = textureColor.sample(textureSampler, input.textureCoordinate); // 纹理颜色
//    half4 colorTex = half4(input.pixelColor.x, input.pixelColor.y, input.pixelColor.z, 1); // 顶点颜色，方便调试
return float4(colorTex);
}```

#### 注意事项

• 方案1、图元朝向做剔除；
```        [renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];
[renderEncoder setCullMode:MTLCullModeBack];```
• 方案2、深度测试剔除；
```    // 创建深度缓存
MTLDepthStencilDescriptor *depthStencilDescriptor = [MTLDepthStencilDescriptor new];
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionLess;
self.depthStencilState = [self.mtkView.device newDepthStencilStateWithDescriptor:depthStencilDescriptor];

// 然后设置深度测试
[renderEncoder setDepthStencilState:self.depthStencilState];```

#### 附录 ---- 天空盒的另一种简单实现

```    MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm size:image.size.width mipmapped:NO];
self.texture = [self.mtkView.device newTextureWithDescriptor:textureDescriptor];

NSInteger pixels = image.size.width * image.size.width;
if (imageBytes) {
for (int i = 0; i < 6; i++)
{
[self.texture replaceRegion:MTLRegionMake2D(0, 0, image.size.width, image.size.width)
mipmapLevel:0
slice:i
withBytes:imageBytes + (i * pixels * 4)
bytesPerRow:4 * (NSInteger)image.size.width
bytesPerImage:pixels * 4];
}

free(imageBytes);
imageBytes = NULL;
}```

`out.textureCoordinate = vertexArray[vertexID].position.xyz;`

```// 试试代码改为下面这段
out.textureCoordinate = out.clipSpacePosition.xyz;```

### 总结

demo尝试实现天空盒的效果，通过较为复杂的方式，去更好学习天空盒的原理。 通过对顶点、纹理、变换矩阵的处理，能更好掌握图形学中三维空间的理解。 具体的代码在这里

172 篇文章86 人订阅

0 条评论

## 相关文章

1.7K70

27130

521100

### PyTorch 最新版发布：API 变动，增加新特征，多项运算和加载速度提升

【新智元导读】PyTorch 发布了最新版，API 有一些变动，增加了一系列新的特征，多项运算或加载速度提升，而且修改了大量bug。官方文档也提供了一些示例。 ...

64870

11510

428130

15450

33160

41770

26940