首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用SCNTechnique实现运动模糊

用SCNTechnique实现运动模糊
EN

Stack Overflow用户
提问于 2016-06-15 22:12:27
回答 2查看 1.5K关注 0票数 3

几天来,我一直试图让运动模糊和SCNTechnique一起工作,但我还远没有达到我想要的程度。我曾在苹果论坛上问过类似的问题,但他们都死了。因此,我想我应该写一个更详尽的描述,说明我试图用代码实现什么。

安装程序

我和一些敌人在银幕上有个角色。这个字符是一个正方形,敌人是圆圈--这个例子简化了。

我正在使用SceneKit与金属。摄像机修好了。

目标

当圆圈/敌人移动时,我希望他们有模糊的动作。当角色移动时,他不应该。

提出的Idea

编写一个能够处理这个问题的多通道SCNTechnique,我假设这是一种我想做的事情。

传递一个:将敌人/圆圈呈现给另一个缓冲区

传递两个:将运动模糊应用于此缓冲区

通过三个:用原始场景渲染运动模糊缓冲区。

侧注:第二关将是很棘手的,因为我想象每个对象都有它自己的方向,模糊也需要效仿。

下面是我如何在SceneKit中设置对象

代码语言:javascript
复制
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

代码语言:javascript
复制
<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>

它的金属着色器是:

代码语言:javascript
复制
#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

此通行证的技术如下:

代码语言:javascript
复制
<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>

着色器是:

代码语言:javascript
复制
// 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

所以技术是:

代码语言:javascript
复制
<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>

着色器:

代码语言:javascript
复制
    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运动可能会变得模糊,我不想再追求这个了--如果不是的话。

第二个问题是,我哪里出错了?

完成的完整技术:

代码语言:javascript
复制
    <?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>
EN

回答 2

Stack Overflow用户

发布于 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

票数 3
EN

Stack Overflow用户

发布于 2016-06-21 23:16:36

这个答案假设您的对象是嵌入在SceneKit的3D世界中的2D对象(如您描述中的“圆圈”和“方块”所暗示的)。

我认为用SCNTechnique进行多通道渲染可能不是你想要实现的正确解决方案。

我就是这样对待它的:

为圆圈对象的纹理添加一个(相当宽的)透明边距,并使SceneKit圆圈对象更大,以便不透明部分具有正确的大小。透明边缘的宽度是物体可以有的运动模糊轨迹的最大长度。因此,如果圆圈的敌人可以在一个框架内移动相当远,你将需要一个很大的差距。

使用自定义SCNProgram为圆圈对象指定片段着色器。这就是实现运动模糊渲染的地方。您需要将对象速度作为自定义变量传递到着色器中(请参阅SCNProgram文档的“自定义变量”部分)。此外,您还需要将速度矢量转换为圆圈对象的2D纹理坐标系。

然后,在片段着色器中,沿着速度矢量对纹理进行采样,并对采样的颜色进行平均。您可能需要根据速度的大小来选择样本的数量:物体移动得越快,要使用的样本就越多。但是,固定数量的样本也可能很好,只要它足够高。

在上面的插图中,小绿色方格显示正在评估片段着色器的示例像素。这四个黄色的点显示了样品的位置,你可以在那里评估纹理。在这种情况下,2个样本击中纹理的透明边缘,而另2个则落入不透明部分。在这种情况下,输出颜色的alpha值为0.5。

您也可以尝试样本权重,并可能使用更高的权重,为当前的位置(样本在像素内)取决于你的外观。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37846677

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档