프로그래밍/iOS,macOS

[swift] SCNProgram 쉐이더 투명 적용

chance 2024. 10. 31. 16:40

프로그램으로 SCNMaterial 에 쉐이더를 적용하면, fragment 쉐이더에서 알파값을 0으로 주어도 불투명한 색상으로 처리된다. 알파값을 처리하기 위해 프로그램과 머터리얼 설정이 필요.

 

프로그램, 머터리얼 설정

// 쉐이더를 적용할 노드의 머터리얼
guard let targetMaterial = node.geometry?.firstMaterial else {
   // 노드에 geometry 혹은 geometry내에 material 이 없음
   return
}


// 프로그램 생성 및 쉐이더, 불투명도 설정
let program = SCNProgram()
program.vertexFunctionName = "VertexFunc"
program.fragmentFunctionName = "FragmentFunc"
program.isOpaque = false;


// 머터리얼에 프로그램 적용
targetMaterial.shaderModifiers = nil
targetMaterial.program = program

// 투명 설정
targetMaterial.transparencyMode = .rgbZero
targetMaterial.blendMode = .alpha

// depth buffer가 불필요한 경우
targetMaterial.writesToDepthBuffer = false
targetMaterial.readsFromDepthBuffer = false

// culling
targetMaterial.cullMode = .back

위 처럼 설정한 뒤에 fragment 쉐이더에서 float4(0, 0, 0, 0) 값으로 리턴하게 되면 해당 픽셀은 투명하게 처리된다.

 

예를 들어 모델의 정점 x좌표 위치가 원점(0)보다 작은 경우에 재질을 표시하고, 0보다 큰 부분은 투명 처리 하는 쉐이더를 만드는 경우 아래와 같이 구현 가능하다.

// MVP 변환전의 모델 위치를 fragment에 전달하기 위해 
// 버텍스 쉐이더 리턴 구조체에 modelPosition을 추가
typdef struct
{
    float4 position [[ position ]];
    float3 modelPosition;
    float2 texCoords;
} FragmentInput;




// vertex shader
vertex FragmentInput VertexFunc(VertexInput in [[ stage_in ]],
                                constant SCNSceneBuffer& scn_frame [[ buffer(0) ]],
                                constant NodeBuffer& scn_node [[ buffer(1) ]],
                                constatnt MyValue& value [[ buffer(2) ]])
{
    FragmentInput ret;
    
    ret.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    ret.modelPosition = in.position;
    ret.texCoords = in.texCoords;

    return ret;
}


// fragment shader
fragment float4 FragmentFunc(FragmentInput in [[ stage_in ]], 
                             texture2d<float, access::sample> dataTexture [[ texture(0) ]])
{
    constexpr sampler sampler2d(coord::normalized, filter::linear, address::repeat);
    
    float4 color;
    if (in.position.x > 0) {
        color = float4(0, 0, 0, 0);
    } else {
        color = dataTexture.sample(sampler2d, in.texCoords);
    }
    
    return color;
}