为什么将金属着色器渐变作为SCNProgram应用于SceneKit节点比作为MTKView进行照明? [英] why is metal shader gradient lighter as a SCNProgram applied to a SceneKit Node than it is as a MTKView?

查看:192
本文介绍了为什么将金属着色器渐变作为SCNProgram应用于SceneKit节点比作为MTKView进行照明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个由Metal片段着色器生成的渐变,该渐变已应用于平面几何定义的SCNNode.

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

它看起来像这样:

当我将相同的着色器应用于Xcode游乐场中渲染的MTKView时,颜色会变暗.是什么导致Scenekit版本中的颜色变浅?

When I use the same shader applied to a MTKView rendered in an Xcode playground, the colors are darker. What is causing the colors to be lighter in the Scenekit version?

这里是Metal着色器和GameViewController.

Here is the Metal shader and the GameViewController.

着色器:

#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

struct myPlaneNodeBuffer {
    float4x4 modelTransform;
    float4x4 modelViewTransform;
    float4x4 normalTransform;
    float4x4 modelViewProjectionTransform;
    float2x3 boundingBox;
};

typedef struct {
    float3 position [[ attribute(SCNVertexSemanticPosition) ]];
    float2 texCoords [[ attribute(SCNVertexSemanticTexcoord0) ]];
} VertexInput;

struct SimpleVertexWithUV
{
    float4 position [[position]];
    float2 uv;
};


vertex SimpleVertexWithUV gradientVertex(VertexInput in [[ stage_in ]],
                         constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                         constant myPlaneNodeBuffer& scn_node [[buffer(1)]])
{
    SimpleVertexWithUV vert;

    vert.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);

    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);

    float2 resolution = float2(width,height);

    vert.uv = vert.position.xy * 0.5 / resolution;

    vert.uv = 0.5 - vert.uv;

    return vert;
}

fragment float4 gradientFragment(SimpleVertexWithUV in [[stage_in]],
                           constant myPlaneNodeBuffer& scn_node [[buffer(1)]])
{
    float4 fragColor;
    float3 color = mix(float3(1.0, 0.6, 0.1), float3(0.5, 0.8, 1.0), sqrt(1-in.uv.y));
    fragColor = float4(color,1);
    return(fragColor);
}

游戏视图控制器:

import SceneKit
import QuartzCore


class GameViewController: NSViewController {

@IBOutlet weak var gameView: GameView!

override func awakeFromNib(){
    super.awakeFromNib()

    // create a new scene
    let scene = SCNScene()

    // create and add a camera to the scene
    let cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    scene.rootNode.addChildNode(cameraNode)

    // place the camera
    cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

    // turn off default lighting
    self.gameView!.autoenablesDefaultLighting = false

    // set the scene to the view
    self.gameView!.scene = scene

    // allows the user to manipulate the camera
    self.gameView!.allowsCameraControl = true

    // show statistics such as fps and timing information
    self.gameView!.showsStatistics = true

    // configure the view
    self.gameView!.backgroundColor = NSColor.black

    var geometry:SCNGeometry

    geometry = SCNPlane(width:10, height:10)
    let geometryNode = SCNNode(geometry: geometry)

    let program = SCNProgram()
    program.fragmentFunctionName = "gradientFragment"
    program.vertexFunctionName = "gradientVertex"

    let gradientMaterial = SCNMaterial()
    gradientMaterial.program = program
    geometry.materials = [gradientMaterial]


    scene.rootNode.addChildNode(geometryNode)

    }

}

推荐答案

> SceneKit Rendering中的高级会话中,SceneKit现在默认为在线性空间中进行渲染,这需要从照明方程式获得准确的结果.

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.

您看到的差异来自以下事实:在MetalKit情况下,您在sRGB颜色空间中提供了颜色分量(红色,绿色和蓝色值),而在SceneKit情况下,您在线性中提供了完全相同的分量. sRGB色彩空间.

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).

如果要在gamma空间中渐变,则需要将颜色分量转换为线性,因为这就是SceneKit的工作方式.从金属着色语言中获取转换公式规范,这是一个解决方案:

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);
}

这篇关于为什么将金属着色器渐变作为SCNProgram应用于SceneKit节点比作为MTKView进行照明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆