#3d #shader #scenekit #metal
#3D #шейдер #scenekit #Металлические
Вопрос:
TL; DR:
Как можно ссылаться на символ sampler2D «не на диске» и передавать его в SCNTechnique
? Мой метод работает, если я ссылаюсь на изображение из моего пакета, но если я этого не сделаю, я не смогу найти способ передать существующий id<MTLTexture>
символ сэмплера, созданный моей техникой.
Долго:
У меня есть действующий рабочий SCNTechnique
, который использует пользовательский символ sampler2D в качестве входных данных для моего прохождения фрагмента металла. Я пытаюсь передать внешний (не из Scenekit) id<MTLTexture>
, который я получаю от аппаратного датчика в качестве входных данных при прохождении после обработки.
Следуя SCNShadable
документам, в которых указано, что id<MTLTexture>
может быть передан в качестве ввода шейдера через SCNMaterialProperty
который имеет соответствующий набор содержимого. Это на 100% работает при передаче модификатора шейдера, но не работает для SCNTecnique
!
let texture = CVMetalTextureGetTexture(textureRef!)
if self.material == nil
{
self.material = SCNMaterialProperty(contents:texture)
}
else
{
self.material?.contents = texture
}
self.currentTechnique?.setObject(self.material, forKeyedSubscript: "myTextureSamplerSymbol" as NSCopying)
Для SCNTechnique
я получаю журналы ошибок, указывающие «Нет хранилища для текстуры», а запись рамки металлического графического процессора указывает на то, что для сэмплера установлена текстура белого цвета размером 4×4 пикселя по умолчанию (предположительно из SCNTecnique
?). Тем не менее, я смог проверить, что мой пользовательский id<MTLTexture>
действителен и имеет содержимое в отладчике — его формат, ширина, высота и содержимое соответствуют ожиданиям, я просто не могу правильно ссылаться на произвольную текстуру в передаче scene kit technique.
Если я объявлю свой символ в SCNTechnique
файле plist для ссылки на изображение следующим образом:
<dict>
<key>type</key>
<string>sampler2D</string>
<key>image</key>
<string>star.png</string>
</dict>
И мои входные данные проходят следующим образом:
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>depthSampler</key>
<string>DEPTH</string>
<key> myTextureSampler</key>
<dict>
<key>target</key>
<string> myTextureSamplerSymbol </string>
</dict>
</dict>
Тогда мой проход работает, и текстура star.png ссылается.
Кто-нибудь получил что-то подобное для работы?
Спасибо.
Ответ №1:
Я уверен, что это сработало.
Быстрый код для настройки MTLTexture
и установки его в SCNTechnique
.
let tech:SCNTechnique = getTechnique()
let textureLoader = MTKTextureLoader(device: MTLCreateSystemDefaultDevice()!)
let filePath = Bundle.main.url(forResource: "gradient", withExtension: "png")!
do {
let gradTexture: MTLTexture = try textureLoader.newTexture(URL: filePath, options: nil)
let matPropTexture = SCNMaterialProperty(contents: gradTexture)
tech.setObject(matPropTexture, forKeyedSubscript: "myTexture" as NSCopying)
} catch {
print("Unexpected error: (error).")
}
scnView.technique = tech
gradient.png
это изображение с цветовым градиентом 256 x 1px (синий -> зеленый -> красный), которое я использую для сопоставления одного значения с псевдо-цветом.
(например; )
Вот полное определение прохождения техники из passes dict в моем списке.
<key>mix_outline</key>
<dict>
<key>draw</key>
<string>DRAW_QUAD</string>
<key>metalVertexShader</key>
<string>pass_through_vertex</string>
<key>metalFragmentShader</key>
<string>mix_outline_fragment</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>color_scene</string>
<key>depthSampler</key>
<string>depth_outline</string>
<key>myTextureSampler</key>
<string>myTexture</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>COLOR</string>
</dict>
</dict>
myTexture
также должно быть определено в разделе символов списка методов.
<key>symbols</key>
<dict>
<key>myTexture</key>
<dict>
<key>type</key>
<string>sampler2D</string>
</dict>
</dict>
Я тоже видел сообщение об ошибке «у pass нет хранилища для ввода myTextureSampler», когда этот блок символов не был включен. Это может быть вашей проблемой?
И, наконец, определение шейдера фрагмента.
fragment half4 mix_outline_fragment(out_vertex_t vert [[stage_in]],
texture2d<float, access::sample> colorSampler [[texture(0)]],
texture2d<float, access::sample> depthSampler [[texture(1)]],
texture2d<float, access::sample> myTextureSampler [[texture(2)]])
{
float4 myTextureColor = myTextureSampler.read(uint2(55, 0));
float4 outline = depthSampler.sample( s, vert.uv);
float4 scene_color = colorSampler.sample( s, vert.uv);
// float4 fragment_color = mix(scene_color, float4(0.0, 0.0, 0.0, 0.0), outline.r);
float4 fragment_color = mix(scene_color, myTextureColor, outline.r);
return half4(fragment_color);
}
В этой технике задействовано несколько других проходов, которые я не включил; просто чтобы дать некоторый контекст, он рендерит сцену за один проход и использует буфер глубины вывода в другом проходе с оператором Sobel для генерации краев в depthSampler
текстуре. Вышеупомянутый шейдер pass fragment рисует эти края поверх исходного рендеринга сцены. Я использовал сплошной черный цвет для краев, но после изучения этого кажется, что я могу прочитать цвет из того, что MTLTexture
я предоставляю SCNTechnique
, и использовать его для краев.
Комментарии:
1. Это супер полезно — спасибо. Я проверю это сегодня и сообщу / отмечу как правильный, если да. Глядя на ваш код и мой, мне непонятно, что, черт возьми, я делаю не так — может быть, срок службы текстуры моего датчика странный? Я отчитаюсь 🙂 Спасибо!
2. Хорошо, ваш подход на 100% работает с текстурой, загруженной с диска, но как принадлежащий приложению (в отличие от принадлежащего SCNTecnique) идентификатор<MTLTexture> , поэтому я собираюсь пометить это как правильное — я подозреваю, что либо при обновлении SCNMaterialPropertyContents, либо время жизни моих текстур виновато в ошибке. Спасибо!
3. Вместо обновления
SCNMaterialProperty.contents
я бы, вероятно, предложил обновить саму MTLTexture (даже если вы добавляете новую текстуру в ту, которую вы передали технике. Другая проблема может заключаться в обновлении содержимого map prop; Я бы убедился, что это что-то вроде метода делегирования updateAtTime .