问题描述
所以我正在使用Metal渲染3个对象:一个立方体,一个导入的模型(看起来像一条龙)和一个应该用作天空盒的球体。
我正在使用3个不同的管道状态在同一MTKView委托绘制函数中呈现每个状态。我使用相同的顶点着色器来处理每个网格,但是我使用了3种不同的片段着色器。
总结代码:
-
在viewDidLoad内,我设置了所有网格和管道状态。
-
在draw函数中,我使用“ Metalstate”(“渲染管线状态”)来渲染立方体。我使用一些制服旋转它,然后在着色器中为其应用草纹理。我还使用着色器中的法线贴图修改了其法线。我可能做错了:我只是将常规法线乘以纹理给出的法线值。
-
在draw函数中,我使用“ skystate”(渲染管道状态)来渲染球体。我不旋转它,但确实对其应用了天空纹理。
-
在draw函数中,我使用“ diamondstate”(我正在渲染自定义菱形模型,但是将其切换为在Sketchfab.com上找到的免费龙模型)来渲染我也可以使用的自定义模型旋转。
着色器。
-
顶点着色器非常简单。我认为唯一要说的是,我假设世界位置等于“投影矩阵*视图矩阵*模型矩阵*顶点位置”。另外,我使用的法线矩阵可能不正确,无法获得“世界法线”。我本质上做了自己的函数,该函数采用四乘四矩阵,并返回三乘三矩阵。我将最后发布该代码。
-
多维数据集片段着色器称为“ fragmentmain”。我认为我没有做任何特别的事情。我只是应用一些灯光和纹理。
-
天空片段着色器称为“ fragmentskymain”。再次,非常简单,我只是将纹理应用于具有向内法线的球体。
-
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中几乎完成所有事情的原因。
这是显示问题的图像。看起来法线即使旋转也不会改变。考虑到龙上面有很多法线,我还希望看到的颜色比一种蓝色阴影还多。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)