why is metal shader gradient lighter as a SCNProgram applied to a SceneKit Node than it is as a MTKView?

前端 未结 3 847
鱼传尺愫
鱼传尺愫 2020-12-05 21:43

I have a gradient, generated by a Metal fragment shader that I\'ve applied to a SCNNode defined by a plane geometry.

It looks like this:

When I use

相关标签:
3条回答
  • 2020-12-05 22:18

    After learning the root cause of this problem, I did a bit more research on the topic and found another solution. Gamma space rendering can be forced application wide by setting SCNDisableLinearSpaceRendering to TRUE in the application's plist.

    0 讨论(0)
  • 2020-12-05 22:22

    I'm not sure, but it looks to me like your calculation of the size of the node is off, leading your .uv to be off, depending on the position of the node.

    You have:

    int width = abs(scn_node.boundingBox[0].x) + abs(scn_node.boundingBox[1].x);
    int height = abs(scn_node.boundingBox[0].y) + abs(scn_node.boundingBox[1].y);
    

    I would think that should be:

    int width = abs(scn_node.boundingBox[0].x - scn_node.boundingBox[1].x);
    int height = abs(scn_node.boundingBox[0].y - scn_node.boundingBox[1].y);
    

    You want the absolute difference between the two extremes, not the sum. The sum gets larger as the node moves right and down, because it effectively includes the position.

    All of that said, isn't the desired (u, v) already provided to you in in.texCoords?

    0 讨论(0)
  • 2020-12-05 22:31

    As explained in the Advances in SceneKit Rendering session from WWDC 2016, SceneKit now defaults to rendering in linear space which is required to have accurate results from lighting equations.

    The difference you see comes from the fact that in the MetalKit case you are providing color components (red, green and blue values) in the sRGB color space, while in the SceneKit case you are providing the exact same components in the linear sRGB color space.

    It's up to you to decide which result is the one you want. Either you want a gradient in linear space (that's what you want if you are interpolating some data) or in gamma space (that's what drawing apps use).

    If you want a gradient in gamma space, you'll need to convert the color components to be linear because that's what SceneKit works with. Taking the conversion formulas from the Metal Shading Language Specification, here's a solution:

    static float srgbToLinear(float c) {
        if (c <= 0.04045)
            return c / 12.92;
        else
            return powr((c + 0.055) / 1.055, 2.4);
    }
    
    fragment float4 gradientFragment(SimpleVertexWithUV in [[stage_in]],
                                     constant myPlaneNodeBuffer& scn_node [[buffer(1)]])
    {
        float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1 - in.uv.y));
    
        color.r = srgbToLinear(color.r);
        color.g = srgbToLinear(color.g);
        color.b = srgbToLinear(color.b);
    
        float4 fragColor = float4(color, 1);
        return(fragColor);
    }
    

    0 讨论(0)
提交回复
热议问题