微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

金属-如何采样球形天窗以进行反射?

如何解决金属-如何采样球形天窗以进行反射?

所以我正在使用Metal渲染3个对象:一个立方体,一个导入的模型(看起来像一条龙)和一个应该用作天空盒的球体。

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

总结代码

  1. 在viewDidLoad内,我设置了所有网格和管道状态。

  2. 在draw函数中,我使用“ Metalstate”(“渲染管线状态”)来渲染立方体。我使用一些制服旋转它,然后在着色器中为其应用草纹理。我还使用着色器中的法线贴图修改了其法线。我可能做错了:我只是将常规法线乘以纹理给出的法线值。

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

  4. 在draw函数中,我使用“ diamondstate”(我正在渲染自定义菱形模型,但是将其切换为在Sketchfab.com上找到的免费龙模型)来渲染我也可以使用的自定义模型旋转。


着色器。

  1. 顶点着色器非常简单。我认为唯一要说的是,我假设世界位置等于“投影矩阵*视图矩阵*模型矩阵*顶点位置”。另外,我使用的法线矩阵可能不正确,无法获得“世界法线”。我本质上做了自己的函数,该函数采用四乘四矩阵,并返回三乘三矩阵。我将最后发布该代码

  2. 多维数据集片段着色器称为“ fragmentmain”。我认为我没有做任何特别的事情。我只是应用一些灯光和纹理。

  3. 天空片段着色器称为“ fragmentskymain”。再次,非常简单,我只是将纹理应用于具有向内法线的球体。

  4. Dragon片段着色器称为“ 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,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,5))
          
          let sunlight = LIGHT(position: float3(1,3,-2),color: float3(1,1,1),specularcolor: float3(1,intensity: 1,type: .sunlight)
          let ambientlight = LIGHT(position: float3(0,0),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))
          encoder?.setVertexBytes(&vertexdata,index: 1)

          encoder?.setVertexBuffer(self.skymesh.vertexBuffers[0].buffer,index: 0)
          encoder?.setFragmentTexture(self.skytexture,index: 2)
          for mesh in self.skymesh.submeshes {
               encoder?.drawIndexedPrimitives(type: .triangle,indexBufferOffset: mesh.indexBuffer.offset)
          }
          
          // SET UNIFORMS FOR CUSTOM MODEL

          
encoder?.setRenderPipelinestate(self.diamondstate)
          
          self.vertexdata.modelmatrix = float4x4(FULLROTATION: float3(0,-1,0))
          self.vertexdata.normalmatrix = self.vertexdata.modelmatrix.returnthreebythree(FOUR: self.vertexdata.modelmatrix)
          encoder?.setVertexBytes(&vertexdata,index: 1)

          encoder?.setVertexBuffer(self.diamondmesh.vertexBuffers[0].buffer,index: 0)
          for mesh in self.diamondmesh.submeshes {
               encoder?.drawIndexedPrimitives(type: .triangle,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,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,inwardnormals: true,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);
     float3 ambientcolor = float3(0,0);
     
     float3 specularcolor = float3(0,0);
     float3 materialspecularcolor = float3(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]],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);
     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,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,1]
          )
          self = matrix
     }
     
     init(TRANSLATE: float3) {
          let matrix = float4x4(
               [1,[TRANSLATE.x,TRANSLATE.y,TRANSLATE.z,1]
          )
          self = matrix
     }
     
     init(SCALE: float3) {
          let matrix = float4x4(
               [SCALE.x,SCALE.y,SCALE.z,1]
          )
          self = matrix
     }
     
     init(XROTATE: Float) {
          let matrix = float4x4(
               [1,cos(XROTATE),sin(XROTATE),-sin(XROTATE),1]
          )
          self = matrix
     }
     
     init(YROTATE: Float) {
          let matrix = float4x4(
               [cos(YROTATE),-sin(YROTATE),[sin(YROTATE),cos(YROTATE),1]
          )
          self = matrix
     }
     
     init(ZROTATE: Float) {
          let matrix = float4x4(
               [cos(ZROTATE),sin(ZROTATE),[-sin(ZROTATE),cos(ZROTATE),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,1 / tan(0.5 * fov),far/(far - near),(far / (far - near)) * -near,0]
          )
          self = matrix
     }
          
     
}

任何对项目组织的评论也将不胜感激。我想使项目尽可能的小,这就是为什么在View Controller中几乎完成所有事情的原因。

这是显示问题的图像。看起来法线即使旋转也不会改变。考虑到龙上面有很多法线,我还希望看到的颜色比一种蓝色阴影还多。

Image

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。