【发布时间】:2017-08-08 13:12:34
【问题描述】:
【问题讨论】:
-
我认为可以使用 iOS 的 SceneKit 而不是 Metal。
-
Metal 提供对 GPU 的最低开销访问。它是 Apple 的一项全新技术。
标签: 3d swift3 sprite-kit scenekit metal
【问题讨论】:
标签: 3d swift3 sprite-kit scenekit metal
需要意识到的是,从表面反射的光是加法:每个光都会贡献一定量的辐照度,而表面的辐射出射度是表面响应的总和到这个能量。我们将使用的数量仍然不是完全基于物理的,但这种洞察力将帮助我们选择不会使最终颜色过于糟糕的值。
为简单起见,我们将坚持使用定向灯。您可以轻松地将其概括为点光源或聚光灯;只需传入您需要的属性并计算每个光源的贡献即可。
首先,删除struct Light 的定义和全局light 值。我们将把它们提升到 uniforms 结构中,以便我们可以从应用程序代码中设置它们。
重新定义“Shared.h”标头中的Light 结构,以便在我们的Objective-C 和Metal 代码中都可以看到它:
#define kMaxLights 4
typedef struct
{
vector_float3 direction;
vector_float3 ambientColor;
vector_float3 diffuseColor;
vector_float3 specularColor;
} Light;
当我们这样做的时候,让我们从使用 simd 命名空间切换到使用 vector_ 和 matrix_ 前缀,只是为了减少我们对 Objective-C++ 的依赖:
typedef struct
{
matrix_float4x4 modelViewProjectionMatrix;
matrix_float4x4 modelViewMatrix;
matrix_float3x3 normalMatrix;
Light lights [kMaxLights];
} Uniforms;
在这里,我们在 uniforms 结构中包含了一个固定大小的灯光数组,因此我们可以指定多达四个灯光,而无需在着色器中进行分支。您可以根据需要减少或增加此数字。
在应用程序代码中(具体来说,updateUniforms 方法),我们可以重新定义第一盏灯以匹配之前的值:
Uniforms uniforms = { 0 };
(将制服结构清零,然后)
uniforms.lights[0].direction = (vector_float3){ 0.13, 0.72, 0.68 };
uniforms.lights[0].ambientColor = (vector_float3){ 0.05, 0.05, 0.05 };
uniforms.lights[0].diffuseColor = (vector_float3){ 0.9, 0.9, 0.9 };
uniforms.lights[0].specularColor = (vector_float3){ 1, 1, 1 };
您现在可以运行该应用来查看与以前相同的结果,其优点是灯光属性现在是可编程的,而不是硬编码的。
此时,我将通过在着色器代码中将环境和漫反射响应设置为白色来切换到白色、有光泽的表面材质:
constant Material material = {
.ambientColor = { 1, 1, 1 },
.diffuseColor = { 1, 1, 1 },
.specularColor = { 1, 1, 1 },
.specularPower = 100
};
让我们修改我们的着色器以循环遍历各种灯光,记住在遇到它的任何地方都要标准化灯光方向,这样我们就不会得到过多的贡献:
fragment float4 fragment_main(ProjectedVertex vert [[stage_in]],
constant Uniforms &uniforms [[buffer(0)]])
{
float3 color(0);
for (int i = 0; i < kMaxLights; ++i) {
constant Light *light = &uniforms.lights[i];
float3 ambientTerm = light->ambientColor * material.ambientColor;
float3 normal = normalize(vert.normal);
float diffuseIntensity = saturate(dot(normal, normalize(light->direction)));
float3 diffuseTerm = light->diffuseColor * material.diffuseColor * diffuseIntensity;
float3 specularTerm(0);
if (diffuseIntensity > 0)
{
float3 eyeDirection = normalize(vert.eye);
float3 halfway = normalize(normalize(light->direction) + eyeDirection);
float specularFactor = pow(saturate(dot(normal, halfway)), material.specularPower);
specularTerm = light->specularColor * material.specularColor * specularFactor;
}
color += ambientTerm + diffuseTerm + specularTerm;
}
return float4(color, 1);
}
现在我们可以定义所有四个灯光的属性,回到 updateUniforms 方法中:
uniforms.lights[0].direction = (vector_float3){ -1, 1, 1 };
uniforms.lights[0].ambientColor = (vector_float3){ 0.1, 0.1, 0.1 };
uniforms.lights[0].diffuseColor = (vector_float3){ 0.7, 0, 0 };
uniforms.lights[0].specularColor = (vector_float3){ 0.15, 0.15, 0.15 };
uniforms.lights[1].direction = (vector_float3){ 1, 1, 1 };
uniforms.lights[1].ambientColor = (vector_float3){ 0 };
uniforms.lights[1].diffuseColor = (vector_float3){ 0, 0.4, 0 };
uniforms.lights[1].specularColor = (vector_float3){ 0.15, 0.15, 0.15 };
uniforms.lights[2].direction = (vector_float3){ -1, -1, 1 };
uniforms.lights[2].ambientColor = (vector_float3){ 0 };
uniforms.lights[2].diffuseColor = (vector_float3){ 0, 0, 0.7 };
uniforms.lights[2].specularColor = (vector_float3){ 0.15, 0.15, 0.15 };
uniforms.lights[3].direction = (vector_float3){ 1, 1, 1 };
uniforms.lights[3].ambientColor = (vector_float3){ 0 };
uniforms.lights[3].diffuseColor = (vector_float3){ 0.1, 0.1, 0.1 };
uniforms.lights[3].specularColor = (vector_float3){ 0.15, 0.15, 0.15 };
结果如下,一个白色茶壶被不同属性的灯光从几个不同的方向照亮:
【讨论】:
SCNNode 只能有一个SCNLight,但您可以在场景中添加任意数量的节点。