Ios 金属-如何对球体天空盒进行采样以进行反射?

Ios 金属-如何对球体天空盒进行采样以进行反射?,ios,3d,metal,fragment-shader,Ios,3d,Metal,Fragment Shader,所以我使用金属渲染了3个对象:一个立方体,一个看起来像龙的导入模型,还有一个应该用作天空盒的球体 我使用3种不同的管道状态在同一个MTKView委托绘制函数中渲染每个管道状态。我使用相同的顶点着色器处理每个网格,但使用3个不同的片段着色器 总结代码: 在viewDidLoad中,我设置了所有网格和管道状态 在draw函数中,我使用“metalstate”(渲染管道状态)来渲染立方体。我通过使用一些制服旋转它,并在着色器中对其应用草纹理。我还使用着色器内的法线贴图修改其法线。我可能做错了:我只是

所以我使用金属渲染了3个对象:一个立方体,一个看起来像龙的导入模型,还有一个应该用作天空盒的球体

我使用3种不同的管道状态在同一个MTKView委托绘制函数中渲染每个管道状态。我使用相同的顶点着色器处理每个网格,但使用3个不同的片段着色器

总结代码:

  • 在viewDidLoad中,我设置了所有网格和管道状态

  • 在draw函数中,我使用“metalstate”(渲染管道状态)来渲染立方体。我通过使用一些制服旋转它,并在着色器中对其应用草纹理。我还使用着色器内的法线贴图修改其法线。我可能做错了:我只是用纹理给出的法线值乘以正则法线

  • 在draw函数中,我使用“skystate”(渲染管道状态)渲染球体。我不会旋转它,但会对其应用天空纹理

  • 在draw函数中,我使用“diamondstate”(我渲染的是一个自定义的菱形模型,但我将其切换到Sketchfab.com上找到的自由龙模型)来渲染我也旋转的自定义模型


  • 着色器

  • 顶点着色器非常简单。我想说的唯一一件事是,我假设世界位置等于“投影矩阵*视图矩阵*模型矩阵*顶点位置”。此外,我使用可能不正确的法线矩阵来获取“世界法线”。我基本上创建了我自己的函数,它接受四乘四矩阵并返回三乘三矩阵。我会在最后发布代码

  • 立方体片段着色器称为“fragmentmain”。我想我没有做什么特别的事。我只是简单地应用一些灯光和纹理

  • 天空片段着色器称为“fragmentskymain”。同样,非常简单,我只是将纹理应用到球体上,球体上有向内法线

  • 龙碎片着色器被称为“fragmentdiamondmain”(非常抱歉命名错误)。在这幅图中,我加载了相同的天空纹理,并使用纹理坐标对其进行采样,纹理坐标是通过反射视图方向和顶点的法线(VIN)来计算的。通过从顶点的世界位置减去摄影机位置来计算视图方向

  • 奇怪的是,应该提供天空纹理颜色的纹理坐标是float3,但我只能使用我正在使用的纹理采样器对float2进行采样

    我认为这可能是问题的根源

    以下是视图控制器代码:

    
    class ViewController: UIViewController, MTKViewDelegate {
    
         
         var metalview: MTKView!
         var metalmesh: MTKMesh!
         var skymesh: MTKMesh!
         var diamondmesh: MTKMesh!
         
         var metaldevice: MTLDevice!
         var metallibrary: MTLLibrary!
         var metalqueue: MTLCommandQueue!
         var metalstate: MTLRenderPipelineState!
         var depthstate: MTLDepthStencilState!
         var skystate: MTLRenderPipelineState!
         var diamondstate: MTLRenderPipelineState!
         
         var skytexture: MTLTexture!
         var grasstexture: MTLTexture!
         var grassnormaltexture: MTLTexture!
         
         
         var timer = Float.zero
         var vertexdata = UNIFORMS(projectionmatrix: float4x4(), viewmatrix: float4x4(), modelmatrix: float4x4(), normalmatrix: float3x3())
         var fragmentdata = FRAGMENTUNIFORMS(lightcount: 0, cameraposition: float3(0,0,5))
         var lights = [LIGHT]()
         
         override var prefersStatusBarHidden: Bool { return true }
         override var prefersHomeIndicatorAutoHidden: Bool { return true }
         override func viewDidLoad() {
              super.viewDidLoad()
    
              self.view.backgroundColor = .white
              
              self.metaldevice = MTLCreateSystemDefaultDevice()
              self.metalview = MTKView()
              self.metalview.frame = UIScreen.main.bounds
              self.metalview.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
              self.metalview.colorPixelFormat = .bgra8Unorm
              self.metalview.depthStencilPixelFormat = .depth32Float
              self.metalview.delegate = self
              self.metalview.device = self.metaldevice
              
              self.metallibrary = self.metaldevice.makeDefaultLibrary()
              self.metalqueue = self.metaldevice.makeCommandQueue()
              self.metalmesh = self.returncube()
              self.skymesh = self.returnsphere()
              
              
              // CUSTOM MODEL
              
              let vertexdescriptor = MTLVertexDescriptor()
              vertexdescriptor.attributes[0].format = .float3
              vertexdescriptor.attributes[0].offset = 0
              vertexdescriptor.attributes[0].bufferIndex = 0
              vertexdescriptor.layouts[0].stride = (MemoryLayout<float3>.stride * 2) + MemoryLayout<float2>.stride
              
              let meshdescriptor = MTKModelIOVertexDescriptorFromMetal(vertexdescriptor)
              (meshdescriptor.attributes[0] as! MDLVertexAttribute).name = MDLVertexAttributePosition
              meshdescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal, format: .float3, offset: MemoryLayout<float3>.stride, bufferIndex: 0)
              meshdescriptor.attributes[2] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: .float2, offset: MemoryLayout<float3>.stride * 2, bufferIndex: 0)
    
              let allocator = MTKMeshBufferAllocator(device: self.metaldevice)
              let url = Bundle.main.url(forResource: "dragon", withExtension: "obj")
              let asset = MDLAsset(url: url, vertexDescriptor: meshdescriptor, bufferAllocator: allocator)
              let model = asset.childObjects(of: MDLMesh.self).first as! MDLMesh
              model.addNormals(withAttributeNamed: MDLVertexAttributeNormal, creaseThreshold: 1)
              self.diamondmesh = try! MTKMesh(mesh: model, device: self.metaldevice)
                       
              // Metalstate (cube) render pipeline state 
              
              let vertexfunction = self.metallibrary.makeFunction(name: "vertexmain")
              let fragmentfunction = self.metallibrary.makeFunction(name: "fragmentmain")
              let descriptor = MTLRenderPipelineDescriptor()
              descriptor.vertexFunction = vertexfunction
              descriptor.fragmentFunction = fragmentfunction
              descriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
              descriptor.depthAttachmentPixelFormat = .depth32Float
              descriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(self.metalmesh.vertexDescriptor)
              
              // SKY STATE
              let skyfragmentfunction = self.metallibrary.makeFunction(name: "fragmentskymain")
              let skydescriptor = MTLRenderPipelineDescriptor()
              skydescriptor.vertexFunction = vertexfunction
              skydescriptor.fragmentFunction = skyfragmentfunction
              skydescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
              skydescriptor.depthAttachmentPixelFormat = .depth32Float
              skydescriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(self.skymesh.vertexDescriptor)
              
              self.skystate = try! self.metaldevice.makeRenderPipelineState(descriptor: skydescriptor)
              
              
              // DIAMOND STATE
              let diamondfragmentfunction = self.metallibrary.makeFunction(name: "fragmentdiamondmain")
              let diamonddescriptor = MTLRenderPipelineDescriptor()
              diamonddescriptor.vertexFunction = vertexfunction
              diamonddescriptor.fragmentFunction = diamondfragmentfunction
              diamonddescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
              diamonddescriptor.depthAttachmentPixelFormat = .depth32Float
              diamonddescriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(self.diamondmesh.vertexDescriptor)
              
              self.diamondstate = try! self.metaldevice.makeRenderPipelineState(descriptor: diamonddescriptor)
              //
              
              let depthdescriptor = MTLDepthStencilDescriptor()
              depthdescriptor.depthCompareFunction = .less
              depthdescriptor.isDepthWriteEnabled = true
              self.depthstate = try! self.metaldevice.makeDepthStencilState(descriptor: depthdescriptor)
              
              
              // TEXTURES //
              let textureloader = MTKTextureLoader(device: self.metaldevice)
              let grassdata = UIImage(named: "GRASS")!.pngData()!
              let grassnormaldata = UIImage(named: "GRASSNORMAL")!.pngData()!
              self.grasstexture = try! textureloader.newTexture(data: grassdata, options: [:])
              self.grassnormaltexture = try! textureloader.newTexture(data: grassnormaldata, options: [:])
              let skydata = UIImage(named: "SKY")!.pngData()!
              self.skytexture = try! textureloader.newTexture(data: skydata, options: [:])
              
              
              // UNIFORMS
              self.vertexdata.projectionmatrix = float4x4(PROJECTION: 120, far: 1000, near: 0.001, aspect: Float(UIScreen.main.bounds.width / UIScreen.main.bounds.height))
              self.vertexdata.viewmatrix = float4x4(TRANSLATE: float3(0,0,5))
              
              let sunlight = LIGHT(position: float3(1,3,-2), color: float3(1,1,1), specularcolor: float3(1,1,1), intensity: 1, type: .sunlight)
              let ambientlight = LIGHT(position: float3(0,0,0), color: float3(1,1,1), specularcolor: float3(1,1,1), intensity: 0.05, type: .ambientlight)
              lights.append(sunlight)
              lights.append(ambientlight)
              self.fragmentdata.lightcount = uint(lights.count)
              
              // // // // // // // // //
              
              self.metalstate = try! self.metaldevice.makeRenderPipelineState(descriptor: descriptor)
              
              self.view.addSubview(self.metalview)
    
         }
    
    
         func draw(in view: MTKView) {
              let descriptor = self.metalview.currentRenderPassDescriptor
              let buffer = self.metalqueue.makeCommandBuffer()
              let encoder = buffer?.makeRenderCommandEncoder(descriptor: descriptor!)
              encoder?.setRenderPipelineState(self.metalstate)
              encoder?.setDepthStencilState(self.depthstate)
              
              // SET UNIFORMS FOR CUBE (METALSTATE RPS)
              timer += 0.01
              self.vertexdata.modelmatrix = float4x4(FULLROTATION: float3(0, timer / 2, 0)) * float4x4(TRANSLATE: float3(0,-2,0))
              self.vertexdata.normalmatrix = self.vertexdata.modelmatrix.returnthreebythree(FOUR: self.vertexdata.modelmatrix)
              
              encoder?.setVertexBuffer(self.metalmesh.vertexBuffers[0].buffer, offset: 0, index: 0)
              encoder?.setVertexBytes(&vertexdata, length: MemoryLayout<UNIFORMS>.stride, index: 1)
              encoder?.setFragmentBytes(&fragmentdata, length: MemoryLayout<FRAGMENTUNIFORMS>.stride, index: 2)
              encoder?.setFragmentBytes(&lights, length: MemoryLayout<LIGHT>.stride * lights.count, index: 3)
              encoder?.setFragmentTexture(self.grasstexture, index: 0)
              encoder?.setFragmentTexture(self.grassnormaltexture, index: 1)
              for mesh in self.metalmesh.submeshes {
                   encoder?.drawIndexedPrimitives(type: .triangle, indexCount: mesh.indexCount, indexType: mesh.indexType, indexBuffer: mesh.indexBuffer.buffer, indexBufferOffset: mesh.indexBuffer.offset)
              }
              
    
    
              // SET UNIFORMS FOR SKY SPHERE 
    
              encoder?.setRenderPipelineState(self.skystate)
              self.vertexdata.modelmatrix = float4x4(FULLROTATION: float3(0, 0, 0)) * float4x4(TRANSLATE: float3(0,-2,0))
              encoder?.setVertexBytes(&vertexdata, length: MemoryLayout<UNIFORMS>.stride, index: 1)
    
              encoder?.setVertexBuffer(self.skymesh.vertexBuffers[0].buffer, offset: 0, index: 0)
              encoder?.setFragmentTexture(self.skytexture, index: 2)
              for mesh in self.skymesh.submeshes {
                   encoder?.drawIndexedPrimitives(type: .triangle, indexCount: mesh.indexCount, indexType: mesh.indexType, indexBuffer: mesh.indexBuffer.buffer, indexBufferOffset: mesh.indexBuffer.offset)
              }
              
              // SET UNIFORMS FOR CUSTOM MODEL
    
              
    encoder?.setRenderPipelineState(self.diamondstate)
              
              self.vertexdata.modelmatrix = float4x4(FULLROTATION: float3(0, timer / 2,  0)) * float4x4(TRANSLATE: float3(0,-1,0))
              self.vertexdata.normalmatrix = self.vertexdata.modelmatrix.returnthreebythree(FOUR: self.vertexdata.modelmatrix)
              encoder?.setVertexBytes(&vertexdata, length: MemoryLayout<UNIFORMS>.stride, index: 1)
    
              encoder?.setVertexBuffer(self.diamondmesh.vertexBuffers[0].buffer, offset: 0, index: 0)
              for mesh in self.diamondmesh.submeshes {
                   encoder?.drawIndexedPrimitives(type: .triangle, indexCount: mesh.indexCount, indexType: mesh.indexType, indexBuffer: mesh.indexBuffer.buffer, indexBufferOffset: mesh.indexBuffer.offset)
              }
              
              // END ENCODING 
    
              encoder?.endEncoding()
              let drawable = self.metalview.currentDrawable
              buffer?.present(drawable!)
              buffer?.commit()
         }
         
         func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
              
         }
         
         func returncube() -> MTKMesh {
              let allocator = MTKMeshBufferAllocator(device: self.metaldevice)
              let model = MDLMesh(boxWithExtent: [1,2,1], segments: [1,1,1], inwardNormals: false, geometryType: .triangles, allocator: allocator)
              let mesh = try! MTKMesh(mesh: model, device: self.metaldevice)
              return mesh
         }
         
         func returnsphere() -> MTKMesh {
              let allocator = MTKMeshBufferAllocator(device: self.metaldevice)
              let model = MDLMesh(sphereWithExtent: [50,50,50], segments: [50,50], inwardNormals: true, geometryType: .triangles, allocator: allocator)
              let mesh = try! MTKMesh(mesh: model, device: self.metaldevice)
              return mesh
         }
    }
    
    
    对项目组织的任何意见也将不胜感激。我喜欢让项目尽可能小,这就是为什么几乎所有事情都是在视图控制器中完成的

    这是一张显示问题的图片。看起来法线即使在旋转时也不会改变。我也希望看到更多的颜色比一个蓝色的阴影,因为龙有很多法线

    #include <metal_stdlib>
    using namespace metal;
    #include <simd/simd.h>
    
    
    struct VIN {
         float4 position [[attribute(0)]];
         float3 normal [[attribute(1)]];
         float2 coords [[attribute(2)]];
    };
    
    struct VOUT {
         float4 position [[position]];
         float3 normal;
         float2 coords;
    };
    
    struct UNIFORMS {
         float4x4 projectionmatrix;
         float4x4 viewmatrix;
         float4x4 modelmatrix;
         float3x3 normalmatrix;
    };
    
    struct FRAGMENTUNIFORMS {
         uint lightcount;
         float3 cameraposition;
    };
    
    enum LIGHTTYPE {
         unused = 0,
         sunlight = 1,
         ambientlight = 2
    };
    
    struct LIGHT {
         float3 position;
         float3 color;
         float3 specularcolor;
         float intensity;
         LIGHTTYPE type;
    };
    
    
    
    
    
    
    
    vertex VOUT vertexmain(const VIN in [[stage_in]],
                           constant UNIFORMS &data [[buffer(1)]])
    {
         VOUT out;
         out.position = data.projectionmatrix * data.viewmatrix * data.modelmatrix *  in.position;
         out.normal = data.normalmatrix * in.normal;
         out.coords = in.coords;
         return out;
    }
    
    fragment float4 fragmentmain(const VOUT in [[stage_in]],
                                 constant FRAGMENTUNIFORMS &data [[buffer(2)]],
                                 constant LIGHT *lights [[buffer(3)]],
                                 texture2d<float> grasstexture [[texture(0)]],
                                 texture2d<float> grassnormaltexture [[texture(1)]])
    {
    
         constexpr sampler texturesampler;
    
         float3 basecolor = grasstexture.sample(texturesampler, in.coords).rgb;
         float3 diffusecolor = float3(0,0,0);
         float3 ambientcolor = float3(0,0,0);
         
         float3 specularcolor = float3(0,0,0);
         float3 materialspecularcolor = float3(0,0,0);
         float shine = 5;
         
         float3 mappednormal = grassnormaltexture.sample(texturesampler, in.coords).xyz;
         float3 normaldirection = normalize(mappednormal * in.normal);
         
         for (uint i = 0 ; i < data.lightcount ; i++) {
              LIGHT light = lights[i];
              
              if (light.type == sunlight) {
                   float3 lightdirection = normalize(-light.position);
                   float diffuseintensity = saturate(-dot(lightdirection, normaldirection));
                   diffusecolor = light.color * basecolor * diffuseintensity;
                   
                   if (diffuseintensity > 0) {
                        float3 reflection = reflect(lightdirection, normaldirection);
                        float3 cameradirection = normalize(in.position.xyz - data.cameraposition);
                        float specularintensity = pow(saturate(-dot(reflection, cameradirection)), shine);
                        specularcolor = light.specularcolor * materialspecularcolor * specularintensity;
                   }               
              } else if (light.type == ambientlight) {
                   ambientcolor = light.color * light.intensity;
              }
         }
    
         float3 color = diffusecolor + ambientcolor + specularcolor;
         return float4(color, 1);
    }
    
    
         
         
         
         
    fragment float4 fragmentskymain(const VOUT in [[stage_in]],
                                    texture2d<float> skytexture [[texture(2)]])
    {
         constexpr sampler texturesampler;
         float3 color = skytexture.sample(texturesampler, in.coords).rgb;
         
         return float4(color, 1);
    }
         
         
         
         
    fragment float4 fragmentdiamondmain(const VOUT in [[stage_in]],
                                        constant FRAGMENTUNIFORMS &data [[buffer(2)]],
                                        constant LIGHT *lights [[buffer(3)]],
                                        texture2d<float> skytexture [[texture(2)]])
    {
         
         float3 viewdirection = in.position.xyz - data.cameraposition;
         float3 texturecoords = reflect(viewdirection, in.normal);
         
         constexpr sampler texturesampler;
         float3 skycolor = skytexture.sample(texturesampler, texturecoords.xy).rgb;
         
         
         float3 basecolor = skycolor;
         float3 diffusecolor = float3(0,0,0);
         float3 ambientcolor = float3(0,0,0);
         
         float3 specularcolor = float3(0,0,0);
         float3 materialspecularcolor = float3(0,0,0);
         float shine = 5;
         
         float3 normaldirection = normalize(in.normal);
         
         for (uint i = 0 ; i < data.lightcount ; i++) {
              LIGHT light = lights[i];
              
              if (light.type == sunlight) {
                   float3 lightdirection = normalize(-light.position);
                   float diffuseintensity = saturate(-dot(lightdirection, normaldirection));
                   diffusecolor = light.color * basecolor * diffuseintensity;
                   
                   if (diffuseintensity > 0) {
                        float3 reflection = reflect(lightdirection, normaldirection);
                        float3 cameradirection = normalize(in.position.xyz - data.cameraposition);
                        float specularintensity = pow(saturate(-dot(reflection, cameradirection)), shine);
                        specularcolor = light.specularcolor * materialspecularcolor * specularintensity;
                   }
              } else if (light.type == ambientlight) {
                   ambientcolor = light.color * light.intensity;
              }
         }
    
         float3 color = diffusecolor + ambientcolor + specularcolor;
         return float4(color, 1);
    
    }
    
    
    
    extension float4x4 {
         
         func returnthreebythree(FOUR: float4x4) -> float3x3 {
              let matrix = float3x3(
                   [FOUR.columns.0.x, FOUR.columns.0.y, FOUR.columns.0.z],
                   [FOUR.columns.1.x, FOUR.columns.1.y, FOUR.columns.1.z],
                   [FOUR.columns.2.x, FOUR.columns.2.y, FOUR.columns.2.z]
              )
              return matrix
         }
         
         
         init(IDENTITY: Float) {
              let matrix = float4x4(
                   [1, 0, 0, 0],
                   [0, 1, 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]
              )
              self = matrix
         }
         
         init(TRANSLATE: float3) {
              let matrix = float4x4(
                   [1, 0, 0, 0],
                   [0, 1, 0, 0],
                   [0, 0, 1, 0],
                   [TRANSLATE.x, TRANSLATE.y, TRANSLATE.z, 1]
              )
              self = matrix
         }
         
         init(SCALE: float3) {
              let matrix = float4x4(
                   [SCALE.x, 0, 0, 0],
                   [0, SCALE.y, 0 , 0],
                   [0, 0, SCALE.z, 0],
                   [0, 0 , 0, 1]
              )
              self = matrix
         }
         
         init(XROTATE: Float) {
              let matrix = float4x4(
                   [1, 0, 0, 0],
                   [0, cos(XROTATE), sin(XROTATE), 0],
                   [0, -sin(XROTATE), cos(XROTATE), 0],
                   [0, 0, 0, 1]
              )
              self = matrix
         }
         
         init(YROTATE: Float) {
              let matrix = float4x4(
                   [cos(YROTATE), 0, -sin(YROTATE), 0],
                   [0, 1, 0, 0],
                   [sin(YROTATE), 0, cos(YROTATE), 0],
                   [0, 0, 0, 1]
              )
              self = matrix
         }
         
         init(ZROTATE: Float) {
              let matrix = float4x4(
                   [cos(ZROTATE), sin(ZROTATE), 0, 0],
                   [-sin(ZROTATE), cos(ZROTATE), 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]
              )
              self = matrix
         }
         
         init(FULLROTATION: float3) {
              let xrotation = float4x4(XROTATE: FULLROTATION.x)
              let yrotation = float4x4(YROTATE: FULLROTATION.y)
              let zrotation = float4x4(ZROTATE: FULLROTATION.z)
              self = xrotation * yrotation * zrotation
         }
         
         init(PROJECTION fov: Float, far: Float, near: Float, aspect: Float) {
              let matrix = float4x4(
                   [ ((1 / tan(0.5 * fov)) / aspect),     0,                       0,                             0],
                   [ 0,                                   1 / tan(0.5 * fov),      0,                             0],
                   [ 0,                                   0,                       far/(far - near),              1],
                   [ 0,                                   0,                       (far / (far - near)) * -near,  0]
              )
              self = matrix
         }
              
         
    }