我正在做一个游戏,在这个游戏中,我使用一个计算着色器程序化地生成行星,这个计算着色器基于我下载的噪声函数中的噪声构建一个变形的球体。整个系统在着色器中工作正常。然而,我希望能够在c#中对噪声函数进行采样,而无需与GPU进行交互,因此我尝试将该函数转换为在c#中工作的东西,乍一看它似乎可以工作,并且功能完美地作为单纯噪声,但它的形状始终与GPU版本不同。我不确定我是否在某个地方有一些错误的数学方法,或者确实有任何关于问题可能是什么的想法。
Image of both results
下面是HLSL中函数的代码
float3 mod289(float3 x)
{
return x - floor(x / 289.0) * 289.0;
}
float4 mod289(float4 x)
{
return x - floor(x / 289.0) * 289.0;
}
float4 permute(float4 x)
{
return mod289((x * 34.0 + 1.0) * x);
}
float4 taylorInvSqrt(float4 r)
{
return 1.79284291400159 - r * 0.85373472095314;
}
float snoise(float3 v)
{
const float2 C = float2(1.0 / 6.0, 1.0 / 3.0);
// First corner
float3 i = floor(v + dot(v, C.yyy));
float3 x0 = (v - i) + dot(i, C.xxx);
// Other corners
float3 g = step(x0.yzx, x0);
float3 l = 1.0 - g;
float3 i1 = min(g, l.zxy);
float3 i2 = max(g, l.zxy);
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
float3 x1 = x0 - i1 + C.xxx;
float3 x2 = x0 - i2 + C.yyy;
float3 x3 = x0 - 0.5;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
float4 p =
permute(permute(permute(i.z + float4(0.0, i1.z, i2.z, 1.0))
+ i.y + float4(0.0, i1.y, i2.y, 1.0))
+ i.x + float4(0.0, i1.x, i2.x, 1.0));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float4 j = p - 49.0 * floor(p / 49.0); // mod(p,7*7)
float4 x_ = floor(j / 7.0);
float4 y_ = floor(j - 7.0 * x_); // mod(j,N)
float4 x = (x_ * 2.0 + 0.5) / 7.0 - 1.0;
float4 y = (y_ * 2.0 + 0.5) / 7.0 - 1.0;
float4 h = 1.0 - abs(x) - abs(y);
float4 b0 = float4(x.xy, y.xy);
float4 b1 = float4(x.zw, y.zw);
//float4 s0 = float4(lessThan(b0, 0.0)) * 2.0 - 1.0;
//float4 s1 = float4(lessThan(b1, 0.0)) * 2.0 - 1.0;
float4 s0 = floor(b0) * 2.0 + 1.0;
float4 s1 = floor(b1) * 2.0 + 1.0;
float4 sh = -step(h, 0.0);
float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
float4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
float3 g0 = float3(a0.xy, h.x);
float3 g1 = float3(a0.zw, h.y);
float3 g2 = float3(a1.xy, h.z);
float3 g3 = float3(a1.zw, h.w);
// Normalise gradients
float4 norm = taylorInvSqrt(float4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
g0 *= norm.x;
g1 *= norm.y;
g2 *= norm.z;
g3 *= norm.w;
// Mix final noise value
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
m = m * m;
m = m * m;
float4 px = float4(dot(x0, g0), dot(x1, g1), dot(x2, g2), dot(x3, g3));
return 42.0 * dot(m, px);
}
这是我尝试将其改编成可以在C#中采样的东西。我知道,这是远远不理想的格式,但我试图做一个一对一的转换,并没有真正付出太多的注意最佳做法。
using UnityEngine;
using float4 = UnityEngine.Vector4;
using float3 = UnityEngine.Vector3;
using float2 = UnityEngine.Vector2;
public static class NoiseLibrary
{
private static readonly float2 C = new float2(1.0f / 6.0f, 1.0f / 3.0f);
private static float4 floor(float4 input)
{
return new float4(Mathf.Floor(input.x), Mathf.Floor(input.y), Mathf.Floor(input.z), Mathf.Floor(input.w));
}
private static float3 floor(float3 input)
{
return new float3(Mathf.Floor(input.x), Mathf.Floor(input.y), Mathf.Floor(input.z));
}
private static float4 scale(float4 a, float4 b)
{
return new float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
private static float3 mod289(float3 x)
{
return x - floor(x / 289.0f) * 289.0f;
}
private static float4 mod289(float4 x)
{
return x - floor(x / 289.0f) * 289.0f;
}
private static float4 permute(float4 x)
{
return mod289(scale((x * 34.0f + float4.one), x));
}
private static float4 taylorInvSqrt(float4 r)
{
return (float4.one * 1.79284291400159f) - r * 0.85373472095314f;
}
private static float dot(float3 a, float3 b)
{
return float3.Dot(a, b);
}
private static float3 xxx(float3 input)
{
return new float3(input.x, input.x, input.x);
}
private static float3 yyy(float3 input)
{
return new float3(input.y, input.y, input.y);
}
private static float3 yzx(float3 input)
{
return new float3(input.y, input.z, input.x);
}
private static float3 zxy(float3 input)
{
return new float3(input.z, input.x, input.y);
}
private static float3 add(float a, float3 b)
{
return float3.one * a + b;
}
private static float3 add(float3 b, float a)
{
return float3.one * a + b;
}
private static float4 add(float a, float4 b)
{
return float4.one * a + b;
}
private static float4 add(float4 b, float a)
{
return float4.one * a + b;
}
private static float3 step(float3 a, float3 x)
{
return new float3(x.x > a.x ? 1 : 0, x.y > a.y ? 1 : 0, x.z > a.z ? 1 : 0);
}
private static float3 min(float3 a, float3 b)
{
return new float3(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y), Mathf.Min(a.z, b.z));
}
private static float3 max(float3 a, float3 b)
{
return new float3(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y), Mathf.Max(a.z, b.z));
}
private static float4 max(float4 a, float4 b)
{
return new float4(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y), Mathf.Max(a.z, b.z), Mathf.Max(a.w, b.w));
}
private static float4 abs(float4 a)
{
return new float4(Mathf.Abs(a.x), Mathf.Abs(a.y), Mathf.Abs(a.z), Mathf.Abs(a.w));
}
private static float4 xzyw(float4 input)
{
return new float4(input.x, input.z, input.y, input.w);
}
private static float4 xxyy(float4 input)
{
return new float4(input.x, input.x, input.y, input.y);
}
private static float4 zzww(float4 input)
{
return new float4(input.z, input.z, input.w, input.w);
}
private static float dot(float4 a, float4 b)
{
return float4.Dot(a, b);
}
private static float4 step(float4 a, float4 x)
{
return new float4(x.x > a.x ? 1 : 0, x.y > a.y ? 1 : 0, x.z > a.z ? 1 : 0, x.w > a.w ? 1 : 0);
}
public static float snoise(float3 v)
{
// First corner
float3 i = floor(add(dot(v, yyy(C)), v));
float3 x0 = add(v - i, dot(i, xxx(C)));
// Other corners
float3 g = step(yzx(x0), x0);
float3 l = float3.one - g;
float3 i1 = min(g, zxy(l));
float3 i2 = max(g, zxy(l));
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
float3 x1 = x0 - i1 + xxx(C);
float3 x2 = x0 - i2 + yyy(C);
float3 x3 = x0 - (float3.one * 0.5f);
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
float4 p =
permute(add(permute(add(permute(add(i.z, new float4(0, i1.z, i2.z, 1.0f))), i.y) + new float4(0, i1.y, i2.y, 1.0f)), i.x) + new float4(0, i1.x, i2.x, 1.0f));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float4 j = p - 49.0f * floor(p / 49.0f); // mod(p,7*7)
float4 x_ = floor(j / 7.0f);
float4 y_ = floor(j - 7.0f * x_); // mod(j,N)
float4 x = add(add(x_ * 2.0f, 0.5f) / 7.0f, -1.0f);
float4 y = add(add(y_ * 2.0f, 0.5f) / 7.0f, -1.0f);
float4 h = float4.one - abs(x) - abs(y);
float4 b0 = new float4(x.x, x.y, y.x, y.y);
float4 b1 = new float4(x.z, x.w, y.z, y.w);
//float4 s0 = float4(lessThan(b0, 0.0)) * 2.0 - 1.0;
//float4 s1 = float4(lessThan(b1, 0.0)) * 2.0 - 1.0;
float4 s0 = floor(b0) * 2.0f + float4.one;
float4 s1 = floor(b1) * 2.0f + float4.one;
float4 sh = -step(h, float4.zero);
float4 a0 = xzyw(b0) + scale(xzyw(s0), xxyy(sh));
float4 a1 = xzyw(b1) + scale(xzyw(s1), zzww(sh));
float3 g0 = new float3(a0.x, a0.y, h.x);
float3 g1 = new float3(a0.z, a0.w, h.y);
float3 g2 = new float3(a1.x, a1.y, h.z);
float3 g3 = new float3(a1.z, a1.w, h.w);
// Normalise gradients
float4 norm = taylorInvSqrt(new float4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
g0 *= norm.x;
g1 *= norm.y;
g2 *= norm.z;
g3 *= norm.w;
// Mix final noise value
float4 m = max((float4.one * 0.6f) - new float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), float4.zero);
m = scale(m, m);
m = scale(m, m);
float4 px = new float4(dot(x0, g0), dot(x1, g1), dot(x2, g2), dot(x3, g3));
return 42.0f * dot(m, px);
}
}
一开始我想我可能在不同的函数中混淆了不同的向量类型,我仔细检查了一遍,以确保所有的向量都匹配。结果仍然是一个完美的工作噪音函数,但我需要它是相同的gpu产生的。
作为增编:我将单位向量重新定义为float2、float3和float4的原因是我在使用Visual Studio的查找和替换功能时遇到了问题,但这应该与代码或结果无关。
1条答案
按热度按时间vxqlmq5t1#
虽然我无法完成这项工作,但如果任何人在Unity资产商店上遇到类似的问题,可以使This Noise Library以最小的修改执行相同的功能,如果任何人有类似的需求,我建议研究一下。