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