프로그래밍/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;
}