Scenekit Shader - 关闭平滑着色和任何法线插值
Scenekit Shader - Turn off smooth shading, and any normal interpolation
我有一个从 Blender 导出的动画模型 (collada)。根据我的理解,着色器会尝试在动画期间插入法线,我想防止这种情况发生。我的模型非常块状,但 Scenekit 让它像 Apple Product 一样平滑。我想让它看起来像我的世界。
我假设您正在寻找类似于下方左侧所示的平面阴影低多边形外观。
此示例使用 SCNSphere
作为几何源,但它也应该适用于您从 dae 文件加载的几何。
你加载的模型,默认的SCNSphere是由很多面组成的。在平滑模型的情况下,顶点通常跨面共享,每个顶点只能有一个法线。我们需要做的是确保每个面不与相邻面共享顶点,并且每个顶点上使用的法线仅根据面的法线方向计算。
SceneKit 本身没有任何本机功能来执行此操作,但是 Apple 的另一个框架有... ModelIO。
您需要导入 ModelIO。
import ModelIO
import SceneKit.ModelIO
我用来生成上面两个球体的代码如下所示。
let geom = SCNSphere(radius: 0.5)
// convert SceneKit geometry to ModelIO mesh object
let modelMesh = MDLMesh(scnGeometry: geom)
// ensure vertices are not shared with neighbouring faces
modelMesh.makeVerticesUnique()
// replace existing 'smooth' normal definition with an 'non-smoothed' normal
modelMesh.addNormals(withAttributeNamed: "normal", creaseThreshold: 1.0)
// create SceneKit geometry from ModelIO mesh object
let flattenedGeom = SCNGeometry(mdlMesh: modelMesh)
let flattenedNode = SCNNode(geometry: flattenedGeom)
scene.rootNode.addChildNode(flattenedNode)
// smooth sphere for comparison only
let sphereNodeSmooth = SCNNode(geometry: SCNSphere(radius: 0.5))
sphereNodeSmooth.position = SCNVector3Make(0, 1.2, 0)
scene.rootNode.addChildNode(sphereNodeSmooth)
当然,我可能会建议您在 Blender 中修改您的模型,以便以这种方式导出(唯一顶点、非平滑法线)。这将使您的应用程序不必在加载资产时执行比所需更多的处理。不幸的是,我的 Blender 知识有所欠缺。
当对所有相邻面的顶点法线进行平均时,会出现平滑阴影。在这种情况下,渲染引擎通过为相邻面插值颜色来渲染边缘,看起来面相交变得更多 'smooth'.
您可以删除顶点的法线信息以防止出现这种情况。
如果你更喜欢SCNGeometry
SCNGeometry* flatGeoUsingScnKit(SCNGeometry *geo){
NSArray<SCNGeometrySource*> *SourceArr = geo.geometrySources;
NSMutableArray<SCNGeometrySource*> *newSourceArr = [NSMutableArray new];
for (SCNGeometrySource *source in SourceArr) {
if (source.semantic != SCNGeometrySourceSemanticNormal) {
[newSourceArr addObject:source];
}
}
SCNGeometry *flatGeo = [SCNGeometry geometryWithSources:newSourceArr elements:geo.geometryElements];
return flatGeo;
}
或ModelI/O
SCNGeometry* flatGeoUsingModelIO(SCNGeometry *geo){
MDLMesh *mesh = [MDLMesh meshWithSCNGeometry:geo];
MDLMesh *newMesh = [MDLMesh newSubdividedMesh:mesh submeshIndex:0 subdivisionLevels:0];
[newMesh removeAttributeNamed:@"normals"];
//Replace current vertex normals with a non-smooth one. Same result with above line
//creaseThreshold is the cos value of MAX crease angle you can tolerate
//[newMesh addNormalsWithAttributeNamed:@"normals" creaseThreshold:1];
SCNGeometry *flatGeo = [SCNGeometry geometryWithMDLMesh:newMesh];
return flatGeo;
}
我有一个从 Blender 导出的动画模型 (collada)。根据我的理解,着色器会尝试在动画期间插入法线,我想防止这种情况发生。我的模型非常块状,但 Scenekit 让它像 Apple Product 一样平滑。我想让它看起来像我的世界。
我假设您正在寻找类似于下方左侧所示的平面阴影低多边形外观。
此示例使用 SCNSphere
作为几何源,但它也应该适用于您从 dae 文件加载的几何。
你加载的模型,默认的SCNSphere是由很多面组成的。在平滑模型的情况下,顶点通常跨面共享,每个顶点只能有一个法线。我们需要做的是确保每个面不与相邻面共享顶点,并且每个顶点上使用的法线仅根据面的法线方向计算。
SceneKit 本身没有任何本机功能来执行此操作,但是 Apple 的另一个框架有... ModelIO。
您需要导入 ModelIO。
import ModelIO
import SceneKit.ModelIO
我用来生成上面两个球体的代码如下所示。
let geom = SCNSphere(radius: 0.5)
// convert SceneKit geometry to ModelIO mesh object
let modelMesh = MDLMesh(scnGeometry: geom)
// ensure vertices are not shared with neighbouring faces
modelMesh.makeVerticesUnique()
// replace existing 'smooth' normal definition with an 'non-smoothed' normal
modelMesh.addNormals(withAttributeNamed: "normal", creaseThreshold: 1.0)
// create SceneKit geometry from ModelIO mesh object
let flattenedGeom = SCNGeometry(mdlMesh: modelMesh)
let flattenedNode = SCNNode(geometry: flattenedGeom)
scene.rootNode.addChildNode(flattenedNode)
// smooth sphere for comparison only
let sphereNodeSmooth = SCNNode(geometry: SCNSphere(radius: 0.5))
sphereNodeSmooth.position = SCNVector3Make(0, 1.2, 0)
scene.rootNode.addChildNode(sphereNodeSmooth)
当然,我可能会建议您在 Blender 中修改您的模型,以便以这种方式导出(唯一顶点、非平滑法线)。这将使您的应用程序不必在加载资产时执行比所需更多的处理。不幸的是,我的 Blender 知识有所欠缺。
当对所有相邻面的顶点法线进行平均时,会出现平滑阴影。在这种情况下,渲染引擎通过为相邻面插值颜色来渲染边缘,看起来面相交变得更多 'smooth'.
您可以删除顶点的法线信息以防止出现这种情况。
如果你更喜欢SCNGeometry
SCNGeometry* flatGeoUsingScnKit(SCNGeometry *geo){
NSArray<SCNGeometrySource*> *SourceArr = geo.geometrySources;
NSMutableArray<SCNGeometrySource*> *newSourceArr = [NSMutableArray new];
for (SCNGeometrySource *source in SourceArr) {
if (source.semantic != SCNGeometrySourceSemanticNormal) {
[newSourceArr addObject:source];
}
}
SCNGeometry *flatGeo = [SCNGeometry geometryWithSources:newSourceArr elements:geo.geometryElements];
return flatGeo;
}
或ModelI/O
SCNGeometry* flatGeoUsingModelIO(SCNGeometry *geo){
MDLMesh *mesh = [MDLMesh meshWithSCNGeometry:geo];
MDLMesh *newMesh = [MDLMesh newSubdividedMesh:mesh submeshIndex:0 subdivisionLevels:0];
[newMesh removeAttributeNamed:@"normals"];
//Replace current vertex normals with a non-smooth one. Same result with above line
//creaseThreshold is the cos value of MAX crease angle you can tolerate
//[newMesh addNormalsWithAttributeNamed:@"normals" creaseThreshold:1];
SCNGeometry *flatGeo = [SCNGeometry geometryWithMDLMesh:newMesh];
return flatGeo;
}