5

I'm currently learning how to use scenekit, and am having issues with transparent objects. I've written a shader to increase transparency when looking faces head on, and everything works as expected when I choose a white background... transparency with white background

but when I choose a black background the object acts as if it's completely opaque transparency with black background

Here's my view controller code

import Cocoa
import SceneKit
import SceneKit.ModelIO

struct shaderSettings {
    var Color: vector_float4
    var minTransparency: Float
}

class ViewController: NSViewController {

    var scnView: SCNView!
    var scnScene: SCNScene!

    override func viewDidLoad() {
        super.viewDidLoad()
        scnView = self.view as! SCNView
        scnScene = SCNScene()
        scnView.scene = scnScene
        scnView.allowsCameraControl = true
        scnView.backgroundColor = NSColor.black

        //load mesh
        let url = URL(fileURLWithPath: "monkey.obj")
        let monkey = MDLAsset(url: url).object(at: 0)
        let node = SCNNode(mdlObject: monkey)
        node.name = "monkey"

        //use metal shader
        let program = SCNProgram();
        program.vertexFunctionName = "vert";
        program.fragmentFunctionName = "frag";
        program.isOpaque = false

        //setup material
        var s = shaderSettings(Color: vector_float4(1, 0, 0, 1) ,minTransparency: 0.3)
        let mat = SCNMaterial();
        mat.program = program;
        mat.setValue(NSData(bytes: &s, length: MemoryLayout<shaderSettings>.stride), forKey: "s")
        mat.blendMode = SCNBlendMode.alpha
        mat.writesToDepthBuffer = false
        mat.readsFromDepthBuffer = false
        mat.cullMode = SCNCullMode.back

        //create node
        node.geometry!.firstMaterial = mat
        scnScene.rootNode.addChildNode(node)

    }

    override func viewDidDisappear() {
        //quit when window closes
        exit(0)
    }
}

and, while I don't think the issue's in here, here's my shader program anyway

#include <metal_stdlib>
using namespace metal;
#include <metal_geometric>
#include <metal_common>
#include <SceneKit/scn_metal>



struct settings {
    float4 Color;
    float minTransparency;
};


typedef struct {
    float4 pos [[ attribute(SCNVertexSemanticPosition) ]];
    float4 norm  [[ attribute(SCNVertexSemanticNormal) ]];
} Input;

typedef struct {
    float4 pos [[position]];
    float3 camDir;
    float3 norm;
} v2f;

struct nodeBuffer {
    float4x4 modelViewProjectionTransform;
    float4x4 modelViewTransform;
    float4x4 normalTransform;
};

vertex v2f vert(Input in [[ stage_in ]],
                constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                constant nodeBuffer& scn_node [[buffer(1)]]) {
    v2f o;
    o.pos = scn_node.modelViewProjectionTransform * in.pos;
    o.norm = normalize(float3(scn_node.normalTransform * in.norm));
    o.camDir = normalize(float3(scn_node.modelViewTransform*in.pos));
    return o;
}

fragment half4 frag(v2f in [[stage_in]], constant settings& s [[buffer(2)]]) {
    float nDotCam = abs(dot(float3(in.norm), float3(-in.camDir)));
    half4 col;

    col.rgb = half3(s.Color.rgb);
    col.a = half(mix(s.Color.a, s.minTransparency, nDotCam));
    return col;
}

Thanks for your time!

kylengelmann
  • 196
  • 8

1 Answers1

1

I figured it out. I read here that scenekit assumes premultiplied alphas, so I added

col.rgb *= col.a;

before the return in the fragment shader and now it works fine

kylengelmann
  • 196
  • 8