Используйте SCNTechnique с металлическими шейдерами на быстрых игровых площадках
Я хочу использовать SCNTechnique с металлическими шейдерами в Swift Playgrounds на SCNView. Поэтому я попытался скомпилировать мои шейдеры с помощью следующего кода:
guard let metalDevice = sceneView.device else {
return
}
if let path = Bundle.main.path(forResource: "distortTechnique", ofType: "metal") {
do {
let contents = try String(contentsOf: URL(fileURLWithPath: path))
do {
try metalDevice.makeLibrary(source: contents, options: nil)
} catch { error
print(error)
}
} catch {
// contents could not be loaded
}
}
Файл загружен, но я получаю следующую ошибку компилятора:
Error Domain=MTLLibraryErrorDomain Code=3 "Compilation failed:
program_source:11:10: fatal error: 'SceneKit/scn_metal' file not found
#include <SceneKit/scn_metal>
^
" UserInfo={NSLocalizedDescription=Compilation failed:
program_source:11:10: fatal error: 'SceneKit/scn_metal' file not found
#include <SceneKit/scn_metal>
^
}
Похоже, что библиотека "scn_metal" не может быть найдена. Любая идея, как я могу решить это? Та же самая настройка прекрасно работает в проекте iOS-приложения.
Вот код шейдера:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct custom_vertex_t
{
float4 position [[attribute(SCNVertexSemanticPosition)]];
};
constexpr sampler s = sampler(coord::normalized,
address::repeat,
filter::linear);
struct out_vertex_t
{
float4 position [[position]];
float2 uv;
float time;
float random;
};
vertex out_vertex_t pass_through_vertex(custom_vertex_t in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]])
{
out_vertex_t out;
out.position = in.position;
out.uv = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
out.time = scn_frame.time;
return out;
};
fragment half4 pass_through_fragment(out_vertex_t vert [[stage_in]],
texture2d<float, access::sample> colorSampler [[texture(0)]])
{
float4 fragment_color = colorSampler.sample( s, vert.uv);
return half4(fragment_color);
};
fragment half4 distort_fragment(out_vertex_t vert [[stage_in]],
texture2d<float, access::sample> colorSampler [[texture(0)]])
{
float multiplier = sin(vert.time * 0.5);
float effectRadius = 0.75;
float effectAmount = 360. * 2.;
float effectAngle = multiplier * effectAmount;
effectAngle = effectAngle * M_PI_F / 180;
float2 resolution = float2(colorSampler.get_width(), colorSampler.get_height());
float2 center = float2(0.5, 0.5);//iMouse.xy / iResolution.xy;
float2 uv = vert.position.xy / resolution - center;
float len = length(uv * float2(resolution.x / resolution.y, 1.));
float angle = atan2(uv.y, uv.x) + effectAngle * smoothstep(effectRadius, 0., len);
float radius = length(uv);
float4 fragment_color = colorSampler.sample(s, float2(radius * cos(angle), radius * sin(angle)) + center);
return half4(fragment_color);
};
Прикрепленное изображение игровой площадки.
Спасибо!
2 ответа
Мне удалось заставить металлический шейдер работать в качестве SCNProgram на игровой площадке, добавив содержимое SCNMetalDefines вверху металлического файла. Я не уверен scn_metal
имеет больше к этому, чем эти определения. Это было все, что мне нужно для конвейера вершины / фрагмента. Не могли бы вы опубликовать код ваших шейдеров? Или, по крайней мере, не могли бы вы рассказать нам, на что ссылается шейдер? scn_metal
?
#include <metal_stdlib>
using namespace metal;
#ifndef __SCNMetalDefines__
#define __SCNMetalDefines__
enum {
SCNVertexSemanticPosition,
SCNVertexSemanticNormal,
SCNVertexSemanticTangent,
SCNVertexSemanticColor,
SCNVertexSemanticBoneIndices,
SCNVertexSemanticBoneWeights,
SCNVertexSemanticTexcoord0,
SCNVertexSemanticTexcoord1,
SCNVertexSemanticTexcoord2,
SCNVertexSemanticTexcoord3
};
// This structure hold all the informations that are constant through a render pass
// In a shader modifier, it is given both in vertex and fragment stage through an argument named "scn_frame".
struct SCNSceneBuffer {
float4x4 viewTransform;
float4x4 inverseViewTransform; // transform from view space to world space
float4x4 projectionTransform;
float4x4 viewProjectionTransform;
float4x4 viewToCubeTransform; // transform from view space to cube texture space (canonical Y Up space)
float4 ambientLightingColor;
float4 fogColor;
float3 fogParameters; // x:-1/(end-start) y:1-start*x z:exp
float2 inverseResolution;
float time;
float sinTime;
float cosTime;
float random01;
};
// In custom shaders or in shader modifiers, you also have access to node relative information.
// This is done using an argument named "scn_node", which must be a struct with only the necessary fields
// among the following list:
//
// float4x4 modelTransform;
// float4x4 inverseModelTransform;
// float4x4 modelViewTransform;
// float4x4 inverseModelViewTransform;
// float4x4 normalTransform; // This is the inverseTransposeModelViewTransform, need for normal transformation
// float4x4 modelViewProjectionTransform;
// float4x4 inverseModelViewProjectionTransform;
// float2x3 boundingBox;
// float2x3 worldBoundingBox;
#endif /* defined(__SCNMetalDefines__) */
Наконец-то я заработал, предварительно скомпилировав металлическую библиотеку реального проекта приложения, а затем использовал результат default.metallib с включенными функциями шейдеров на игровой площадке.
Это кажется необходимым, так как документация SCNTechnique для вершины и фрагмента "Эти функции должны существовать в стандартной библиотеке приложения."
Немного хакерский, но работает для моих целей.
Конкретные шаги: - создать новый проект приложения для выбранной платформы - настроить список SCNTechnique со всеми определениями - добавить файл.metal с кодом шейдера - архивировать проект - открыть архивированный пакет и скопировать файл default.metallib в папку ресурсов вашей игровой площадки - Теперь просто установите свою технику на SCNView - прибыль.
Еще раз спасибо Оливеру за вашу помощь!