J'ai un modèle animé (collada) exporté depuis Blender. D'après ce que j'ai compris, le shader tente d'interpoler la normale pendant l'animation et je veux empêcher que cela ne se produise. Mon modèle est très bloc mais Scenekit le rend lisse comme un produit Apple. Je veux qu'il ressemble à Minecraft.
Réponses
Trop de publicités?Je suppose que vous recherchez un look low poly avec des ombres plates, similaire à celui illustré à gauche ci-dessous.
Cet exemple utilise un SCNSphere
comme source de géométrie, mais il devrait également fonctionner sur la géométrie que vous avez chargée à partir du fichier dae.
Le modèle que vous avez chargé et la SCNSphere par défaut sont constitués de plusieurs faces. Dans le cas des modèles lisses, les sommets sont souvent partagés entre les faces, chaque sommet ne pouvant avoir qu'une seule normale. Nous devons nous assurer que chaque face ne partage pas de sommets avec les faces voisines et que la normale utilisée pour chaque sommet est calculée en fonction de la direction de la normale de la face uniquement.
SceneKit n'a pas de fonctionnalité native pour faire cela lui-même, mais un autre des frameworks d'Apple le fait... ModelIO.
Vous devez importer ModelIO.
import ModelIO
import SceneKit.ModelIO
Le code que j'ai utilisé pour générer les deux sphères ci-dessus est présenté ci-dessous.
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)
Bien sûr, je vous recommanderais probablement de modifier votre modèle dans Blender pour qu'il puisse être exporté de cette manière (sommets uniques, normales non lissées). Cela éviterait à votre application d'avoir à faire plus de traitement que nécessaire lors du chargement des actifs. Malheureusement, mes connaissances sur Blender sont quelque peu insuffisantes.
L'ombrage lisse est obtenu en calculant la moyenne de la normale d'un sommet avec toutes les faces adjacentes. Dans ce cas, le moteur de rendu rend le bord en interpolant la couleur des faces adjacentes, ce qui donne l'impression que la rencontre des faces devient plus "lisse".
Vous pouvez supprimer les informations sur les normales des sommets pour éviter cela.
Si vous préférez 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;
}
Ou ModèleI/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;
}