gpt4 book ai didi

scenekit - 为什么将 Metal 着色器渐变作为SCNProgram应用于SceneKit节点要比作为MTKView轻一些?

转载 作者:行者123 更新时间:2023-12-04 17:31:25 35 4
gpt4 key购买 nike

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

看起来像这样:

scenekit gradient plane node

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

enter image description here

这是Metal着色器和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);
}

游戏 View Controller :
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)

}

}

最佳答案

如WWDC 2016的Advances in SceneKit Rendering session 中所述,SceneKit现在默认为在线性空间中渲染,这是从照明方程式获得准确结果所必需的。

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

由您决定哪个结果是您想要的结果。您想要在线性空间(如果要插值一些数据)中要的梯度,还是在 Gamma 空间(这是绘图应用程序使用的梯度)中的梯度。

如果要在gamma空间中渐变,则需要将颜色分量转换为线性,因为这就是SceneKit的工作方式。从Metal Shading Language Specification中获取转换公式,这是一个解决方案:

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

enter image description here

关于scenekit - 为什么将 Metal 着色器渐变作为SCNProgram应用于SceneKit节点要比作为MTKView轻一些?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44033605/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com