TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Time for a VisionOS forum? And a question about custom materials...

Forums > SwiftUI

I was hoping by now we might see a VisionOS forum... In the meantime, I've been struggling to figure out a way to programatically draw tetrahedra and then, with vertex colors defined, blend them over the faces. (This would be useful for a variety of computational results, including finite element models.) I found a very helpful post on https://stackoverflow.com/questions/77341804/how-can-i-use-a-scenekit-mesh-in-realitykit that draws a 4-noded tetrahedron, all one color. I've also tried to follow Apple documentation that should lead me to the blended vetex colors with a color triangle, but the example is incomplete, and I just don't know enough about Metal to fill-in the blanks for the triangle, much less get it to work with the tetrahedron faces. See https://developer.apple.com/documentation/realitykit/modifying-realitykit-rendering-using-custom-materials

If someone else is working on this kind of issue and willing to provide a complete example for the color triangle, it would be very much appreciated!

Here's the code, almost exactly the same as from the stackoverflow link, that draws a tetrahedron:

//  ContentView.swift
//  Tet1
//

// From:
//https://stackoverflow.com/questions/77341804/how-can-i-use-a-scenekit-mesh-in-realitykit

import SwiftUI
import RealityKit

struct ContentView: View {
    var tetrahedronEntity = TetrahedronEntity()

    var body: some View {
        let modelEntity: ModelEntity = tetrahedronEntity.createModelEntity()

        RealityView { content in
            modelEntity.scale = [ 0.15, 0.15, 0.15 ]
            content.add(modelEntity)
        }
    }
}

final class TetrahedronEntity: Entity {
    let vertices: [SIMD3<Float>] = [            // A tetrahedron has 4 vertices.
        SIMD3( x:  1.0, y: -1.0, z:  1.0 ),     // vertex = 0
        SIMD3( x:  1.0, y:  1.0, z: -1.0 ),     // vertex = 1
        SIMD3( x: -1.0, y:  1.0, z:  1.0 ),     // vertex = 2
        SIMD3( x: -1.0, y: -1.0, z: -1.0 )      // vertex = 3
    ]

    let counts: [UInt8] = [3,3,3,3] // A tetrahedron has 4 faces, each with 3 corners

    let indices: [UInt32] = [
        0, 1, 2,                    // indices to vertices array for face = 0
        1, 0, 3,                    // indices to vertices array for face = 1
        2, 3, 0,                    // indices to vertices array for face = 2
        3, 2, 1,                    // indices to vertices array for face = 3
    ]

    func createModelEntity() -> ModelEntity {
        var meshDescriptor = MeshDescriptor()
        meshDescriptor.positions = MeshBuffers.Positions(vertices)
        meshDescriptor.primitives = .polygons(counts, indices)
        let mesh: MeshResource = try! .generate(from: [meshDescriptor])
//        let material = UnlitMaterial(color: .blue)
        let material = SimpleMaterial(color: .blue, isMetallic: false)
        let modelEntity = ModelEntity(mesh: mesh, materials: [material])
        modelEntity.components.set(GroundingShadowComponent(castsShadow: true))
        return modelEntity
    }
}

#Preview(windowStyle: .volumetric) {
    ContentView()
}

   

OK, I still think it's time to start a forum for VisionOS, although I've gotten no feedback at all... I have, however, made a bit of progress with my issue of programatically drawing tetrahedra with blended colors across the faces. Unfortunately, this uses the coordinates of the vertex to convert to colors for blending (as shown with the attached ShaderGraph material flowchart). What I'd like to do instead is to define the colors of the vertices progromatically, and then have the colors blended over the faces. If anyone has pointers for that, please let me know.

Here is the current ShaderGraph flowchart:

https://www.icloud.com/iclouddrive/0472XQjUY27dhMnC1fTg2y9DA#SchaderGraph_flowchart

Here is the current code:

//
//  ContentView.swift
//  Tet4ShaderGraph
//
//  Based on very helpful post:
//  https://stackoverflow.com/questions/77341804/how-can-i-use-a-scenekit-mesh-in-realitykit
//

import SwiftUI
import RealityKit
import RealityKitContent

struct ContentView: View {

// A 4-noded tetrahedron has 4 corner vertices
  let vertices: [SIMD3<Float>] = [          // A simple tetrahedron has 4 vertices.
    SIMD3( x:  0.0, y:  0.0, z:  0.0 ),     // 0: vertex
    SIMD3( x:  1.0, y:  0.0, z:  0.0 ),     // 1: vertex
    SIMD3( x:  0.0, y:  1.0, z:  0.0 ),     // 2: vertex
    SIMD3( x:  0.0, y:  0.0, z:  1.0 ),     // 3: vertex
    ]

    let counts: [UInt8] = [3,3,3,3] // A simple tetrahedron has 4 faces, each with 3 corners

    let indices: [UInt32] = [
      0, 2, 1,                    // indices to vertices array for face = 0
      0, 1, 3,                    // indices to vertices array for face = 1
      0, 3, 2,                    // indices to vertices array for face = 2
      3, 1, 2                     // indices to vertices array for face = 3
    ]

  var body: some View {

    RealityView { content in
      var meshDescriptor = MeshDescriptor()
      meshDescriptor.positions = MeshBuffers.Positions(vertices)
      meshDescriptor.primitives = .polygons(counts, indices)

      let mesh: MeshResource = try! .generate(from: [meshDescriptor])
//      let material = UnlitMaterial(
//        color: UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
//      )

      let material = try! await ShaderGraphMaterial(named: "/Root/GradMaterial",
                                                            from: "Scene",
                                                            in: realityKitContentBundle)

      let modelEntity = ModelEntity(mesh: mesh, materials: [material])

//  Add text to identify numbered vertex array (usefule for defining indices array)
      let textMaterial = UnlitMaterial(color: .white)
      for i in 0..<vertices.count {
        let positionNumber = MeshResource.generateText("\(i)", extrusionDepth: 0.005, font: .systemFont(ofSize: 0.05))
        // Create a model with the text mesh and material
        let textModel = ModelEntity(mesh: positionNumber, materials: [textMaterial])
        // Set the position of the model slightly above the vertex
        textModel.position = vertices[i] + SIMD3(-0.02, 0.03, 0)
        // Add the model as a child of the main model
        modelEntity.addChild(textModel)
      }

      //  Let the model cast a shadow
      modelEntity.components.set(GroundingShadowComponent(castsShadow: true))
      //    }
      modelEntity.scale = [ 0.2, 0.2, 0.2]

      content.add(modelEntity)

    }
  }
}

#Preview(windowStyle: .volumetric) {
    ContentView()
}

And here is the output on the VisionOs simulator:

https://www.icloud.com/iclouddrive/0bf0Mi4B04iVLZZnafYXYp7QQ#Tet%5FGradient

   

PS: I thought the two images would be embedded into the post, but instead they link to a download from iCloud... (Suggestions for getting an image into a post correctly would be appreciated!)

   

Still no info about a VisionOS forum, but thanks to help from Apple Techincal Support, I can programatically draw tetrahedra and then, with vertex colors defined, blend them over the faces.

//
//  ContentView.swift
//  Tet_ID_Shader
//  Geometry based on very helpful post:
//  https://stackoverflow.com/questions/77341804/how-can-i-use-a-scenekit-mesh-in-realitykit
//
//  Vertex shadergraph coloring from Apple Techincal Support (Thanks!)
//

import SwiftUI
import RealityKit
import RealityKitContent

struct ContentView: View {

// A 4-noded tetrahedron has 4 corner vertices
  let vertices: [SIMD3<Float>] = [          // A simple tetrahedron has 4 vertices.
    SIMD3( x:  0.0, y:  0.0, z:  0.0 ),     // 0: vertex
    SIMD3( x:  1.0, y:  0.0, z:  0.0 ),     // 1: vertex
    SIMD3( x:  0.0, y:  1.0, z:  0.0 ),     // 2: vertex
    SIMD3( x:  0.0, y:  0.0, z:  1.0 ),     // 3: vertex
    ]

//  let vertices: [SIMD3<Float>] = [            // Alternate tetrahedron
//      SIMD3( x:  1.0, y: -1.0, z:  1.0 ),     // vertex = 0
//      SIMD3( x:  1.0, y:  1.0, z: -1.0 ),     // vertex = 1
//      SIMD3( x: -1.0, y:  1.0, z:  1.0 ),     // vertex = 2
//      SIMD3( x: -1.0, y: -1.0, z: -1.0 )      // vertex = 3
//      ]

    let counts: [UInt8] = [3,3,3,3] // A simple tetrahedron has 4 faces, each with 3 corners

    let indices: [UInt32] = [
      0, 2, 1,                    // "vertices" array for face = 0
      0, 1, 3,                    // "vertices" array for face = 1
      0, 3, 2,                    // "vertices" array for face = 2
      3, 1, 2                     // "vertices" array for face = 3
    ]

  let colors: [SIMD4<Float>] = [
    SIMD4<Float>(0.0, 0.0, 0.0, 1.0), // Black: color for vertex 0
//    SIMD4<Float>(1.0, 1.0, 0.0, 1.0), // Yellow
    SIMD4<Float>(1.0, 0.0, 0.0, 1.0), // Red: color for vertex 1
    SIMD4<Float>(0.0, 1.0, 0.0, 1.0), // Green: color for vertex 2
    SIMD4<Float>(0.0, 0.0, 1.0, 1.0), // Blue: color for vertex 3
  ]

  var body: some View {

    RealityView { content in
      var meshDescriptor = MeshDescriptor()
      meshDescriptor.positions = MeshBuffers.Positions(vertices)
      meshDescriptor.primitives = .polygons(counts, indices)

      let mesh: MeshResource = try! .generate(from: [meshDescriptor])

      var material = try! await ShaderGraphMaterial(named: "/Root/GradMaterial",
                                                            from: "Scene",
                                                            in: realityKitContentBundle)

// Send vertex colors (as vector floats) to the shader graph
      try! material.setParameter(name: "c0", value: .simd4Float(colors[0]))
      try! material.setParameter(name: "c1", value: .simd4Float(colors[1]))
      try! material.setParameter(name: "c2", value: .simd4Float(colors[2]))
      try! material.setParameter(name: "c3", value: .simd4Float(colors[3]))

      let modelEntity = ModelEntity(mesh: mesh, materials: [material])

//  Add text to identify numbered vertex array
//      (useful for defining indices array and de-bugging)
      let textMaterial = UnlitMaterial(color: .white)
      for i in 0..<vertices.count {
        let positionNumber = MeshResource.generateText("\(i)", extrusionDepth: 0.005, font: .systemFont(ofSize: 0.05))
        // Create a model with the text mesh and material
        let textModel = ModelEntity(mesh: positionNumber, materials: [textMaterial])
        // Set the position of the model slightly above the vertex
        textModel.position = vertices[i] + SIMD3(-0.02, 0.03, 0)
        // Add the model as a child of the main model
        modelEntity.addChild(textModel)
      }

//  Let the model cast a shadow
      modelEntity.components.set(GroundingShadowComponent(castsShadow: true))

      modelEntity.scale = [ 0.2, 0.2, 0.2]

      content.add(modelEntity)
    }
  }
}

Here's the key: the shadergraph

Shadergraph for GadMaterial

   

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.