几天来,我一直试图让运动模糊和SCNTechnique一起工作,但我还远没有达到我想要的程度。我曾在苹果论坛上问过类似的问题,但他们都死了。因此,我想我应该写一个更详尽的描述,说明我试图用代码实现什么。
安装程序
我和一些敌人在银幕上有个角色。这个字符是一个正方形,敌人是圆圈--这个例子简化了。
我正在使用SceneKit与金属。摄像机修好了。
目标
当圆圈/敌人移动时,我希望他们有模糊的动作。当角色移动时,他不应该。
提出的Idea
编写一个能够处理这个问题的多通道SCNTechnique,我假设这是一种我想做的事情。
传递一个:将敌人/圆圈呈现给另一个缓冲区
传递两个:将运动模糊应用于此缓冲区
通过三个:用原始场景渲染运动模糊缓冲区。
侧注:第二关将是很棘手的,因为我想象每个对象都有它自己的方向,模糊也需要效仿。
下面是我如何在SceneKit中设置对象
class Character : SCNNode
{
override init() {
super.init()
let img = UIImage(named: "texture1")!
material = SCNMaterial()
material.diffuse.contents = img
material.ambient.contents = img
let geometry = SCNSphere(radius: 40)
geometry.materials = [material]
self.categoryBitMask = 1
}
}
class Enemy : SCNNode
{
override init() {
super.init()
let img = UIImage(named: "texture2")!
material = SCNMaterial()
material.diffuse.contents = img
material.ambient.contents = img
let geometry = SCNSphere(radius: 40)
geometry.materials = [material]
self.categoryBitMask = 2
}
}我可以把这些加到现场,它们看起来很好。我可以用SCNActions移动它们,它们的移动也是正确的。
现在来看看我是如何尝试动作模糊的
第一关
我想为这一关只提取敌人,所以这个技术的这一部分看起来如下所示,请注意类别面具只绘制场景中的敌人。
它将此输出到我的自定义"enemiesColor“目标缓冲区。注: DRAW_SCENE
<key>drawEnemies</key>
<dict>
<key>draw</key>
<string>DRAW_SCENE</string>
<key>includeCategoryMask</key>
<integer>2</integer>
<key>excludeCategoryMask</key>
<integer>1</integer>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>multi_vertex</string>
<key>metalFragmentShader</key>
<string>multi_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
<key>aPos</key>
<string>vertexSymbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>enemiesColor</string>
</dict>
</dict>它的金属着色器是:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct custom_node_t3 {
float4x4 modelTransform;
float4x4 modelViewTransform;
float4x4 normalTransform;
float4x4 modelViewProjectionTransform;
};
struct custom_vertex_t
{
float4 position [[attribute(SCNVertexSemanticPosition)]];
float2 a_texcoord [[ attribute(SCNVertexSemanticTexcoord0) ]];
//SCNGeometrySourceSemanticTexcoord
};
constexpr sampler s = sampler(coord::normalized,
address::repeat,
filter::linear);
struct out_vertex_t
{
float4 position [[position]];
float2 texcoord;
};
vertex out_vertex_t multi_vertex(custom_vertex_t in [[stage_in]],
constant custom_node_t3& scn_node [[buffer(0)]])
{
out_vertex_t out;
out.texcoord = in.a_texcoord;
out.position = scn_node.modelViewProjectionTransform * float4(in.position.xyz, 1.0);
return out;
};
fragment half4 multi_fragment_vert(out_vertex_t vert [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
texture2d<float, access::sample> colorSampler [[texture(0)]])
{
float4 FragmentColor = colorSampler.sample( s, vert.texcoord);
return half4(FragmentColor);
};第二关
我想模糊"enemiesColor“缓冲区,目前它非常粗糙,所以我现在只是使用高斯攻击。
我把"enemiesColor“缓冲区作为输入并模糊它,输出它为一个新的缓冲区:"enemyColor”注释: DRAW_QUAD
此通行证的技术如下:
<key>blurEnemies</key>
<dict>
<key>draw</key>
<string>DRAW_QUAD</string>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>blur_vertex</string>
<key>metalFragmentShader</key>
<string>blur_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>enemyColor</key>
<string>enemiesColor</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>chrisColor</string>
</dict>
</dict>着色器是:
// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
constant float offset[] = { 0.0, 1.0, 2.0, 3.0, 4.0 };
constant float weight[] = { 0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162 };
vertex out_vertex_t blur_vertex(custom_vertex_t in [[stage_in]],
constant custom_node_t3& scn_node [[buffer(0)]])
{
out_vertex_t out;
out.position = in.position;
out.texcoord = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
return out;
};
fragment half4 blur_fragment_vert(out_vertex_t vert [[stage_in]],
texture2d<float, access::sample> colorSampler [[texture(0)]],
texture2d<float, access::sample> enemyColor [[texture(1)]])
{
float4 enemySample = enemyColor.sample(s, vert.texcoord);
if (enemySample.a == 0)
{
//gl_LastFragData[0]
return half4(0.0 ,1.0 ,0.0, 0.5);
}
float4 FragmentColor = colorSampler.sample( s, vert.texcoord) * weight[0];
for (int i=1; i<5; i++) {
FragmentColor += colorSampler.sample( s, ( vert.texcoord + float2(0.0, offset[i])/224.0 ) ) * weight[i];
FragmentColor += colorSampler.sample( s, ( vert.texcoord - float2(0.0, offset[i])/224.0 ) ) * weight[i];
}
return half4(FragmentColor);
};三通
当事情变得更加混乱,我想然后应用模糊的"enemyColor“缓冲区与原来的场景。
我的第一个想法是,为什么我不能把结果加起来。我调查了混合模式,但没有发现运气。
然后我想,也许我可以重新渲染场景,并将"enemyColor“和新的”颜色“缓冲区放在一起(如果这样做甚至远程工作的话,可能会有一些优化)。
注: DRAW_SCENE
所以技术是:
<key>blendTogether</key>
<dict>
<key>draw</key>
<string>DRAW_SCENE</string>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>plain_vertex</string>
<key>metalFragmentShader</key>
<string>plain_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>aPos</key>
<string>vertexSymbol</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>COLOR</string>
</dict>
</dict>着色器:
vertex out_vertex_t plain_vertex(custom_vertex_t in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant custom_node_t3& scn_node [[buffer(1)]])
{
out_vertex_t out;
out.position = scn_node.modelViewProjectionTransform * float4(in.position.xyz, 1.0);
out.texcoord = in.a_texcoord;
return out;
};
fragment half4 plain_fragment_vert(out_vertex_t vert [[stage_in]],
texture2d<float, access::sample> colorSampler [[texture(0)]])
{
float4 FragmentColor = colorSampler.sample( s, vert.texcoord);
return half4(FragmentColor);
};在这结束时,我的场景呈现,但我只是没有达到预期的效果。
第一个问题是,如果这样的系统,be...is运动可能会变得模糊,我不想再追求这个了--如果不是的话。
第二个问题是,我哪里出错了?
完成的完整技术:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>passes</key>
<dict>
<key>drawEnemies</key>
<dict>
<key>draw</key>
<string>DRAW_SCENE</string>
<key>includeCategoryMask</key>
<integer>2</integer>
<key>excludeCategoryMask</key>
<integer>1</integer>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>multi_vertex</string>
<key>metalFragmentShader</key>
<string>multi_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
<key>aPos</key>
<string>vertexSymbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>enemiesColor</string>
</dict>
</dict>
<key>blurEnemies</key>
<dict>
<key>draw</key>
<string>DRAW_QUAD</string>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>blur_vertex</string>
<key>metalFragmentShader</key>
<string>blur_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>enemyColor</key>
<string>enemiesColor</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>chrisColor</string>
</dict>
</dict>
<key>blendTogether</key>
<dict>
<key>draw</key>
<string>DRAW_SCENE</string>
<key>program</key>
<string>doesntexist</string>
<key>metalVertexShader</key>
<string>plain_vertex</string>
<key>metalFragmentShader</key>
<string>plain_fragment_vert</string>
<key>inputs</key>
<dict>
<key>colorSampler</key>
<string>COLOR</string>
<key>aPos</key>
<string>vertexSymbol</string>
<key>a_texcoord</key>
<string>a_texcoord-symbol</string>
</dict>
<key>outputs</key>
<dict>
<key>color</key>
<string>COLOR</string>
</dict>
</dict>
</dict>
<key>sequence</key>
<array>
<string>blendTogether</string>
</array>
<key>targets</key>
<dict>
<key>enemiesColor</key>
<dict>
<key>type</key>
<string>color</string>
</dict>
<key>chrisColor</key>
<dict>
<key>type</key>
<string>color</string>
</dict>
</dict>
<key>symbols</key>
<dict>
<key>a_texcoord-symbol</key>
<dict>
<key>semantic</key>
<string>texcoord</string>
</dict>
<key>vertexSymbol</key>
<dict>
<key>semantic</key>
<string>vertex</string>
</dict>
</dict>
</dict>
</plist>发布于 2016-06-16 20:21:53
如果你可以等到macOS塞拉利昂/iOS 10,新的SceneKit高清摄像头支持运动模糊。
视频:https://developer.apple.com/videos/play/wwdc2016/609/
源;https://developer.apple.com/library/prerelease/content/samplecode/Badger/Introduction/Intro.html
发布于 2016-06-21 23:16:36
这个答案假设您的对象是嵌入在SceneKit的3D世界中的2D对象(如您描述中的“圆圈”和“方块”所暗示的)。
我认为用SCNTechnique进行多通道渲染可能不是你想要实现的正确解决方案。
我就是这样对待它的:
为圆圈对象的纹理添加一个(相当宽的)透明边距,并使SceneKit圆圈对象更大,以便不透明部分具有正确的大小。透明边缘的宽度是物体可以有的运动模糊轨迹的最大长度。因此,如果圆圈的敌人可以在一个框架内移动相当远,你将需要一个很大的差距。
使用自定义SCNProgram为圆圈对象指定片段着色器。这就是实现运动模糊渲染的地方。您需要将对象速度作为自定义变量传递到着色器中(请参阅SCNProgram文档的“自定义变量”部分)。此外,您还需要将速度矢量转换为圆圈对象的2D纹理坐标系。
然后,在片段着色器中,沿着速度矢量对纹理进行采样,并对采样的颜色进行平均。您可能需要根据速度的大小来选择样本的数量:物体移动得越快,要使用的样本就越多。但是,固定数量的样本也可能很好,只要它足够高。

在上面的插图中,小绿色方格显示正在评估片段着色器的示例像素。这四个黄色的点显示了样品的位置,你可以在那里评估纹理。在这种情况下,2个样本击中纹理的透明边缘,而另2个则落入不透明部分。在这种情况下,输出颜色的alpha值为0.5。
您也可以尝试样本权重,并可能使用更高的权重,为当前的位置(样本在像素内)取决于你的外观。
https://stackoverflow.com/questions/37846677
复制相似问题