我为太阳表面实现了一个着色器,它使用了来自ashima/webgl-noise的simplex
噪声。但它耗费了太多GPU时间,特别是当我要在移动设备上使用它时。我需要做同样的效果,但使用噪声纹理。我的片段着色器如下:
#ifdef GL_ES
precision highp float;
#endif
precision mediump float;
varying vec2 v_texCoord;
varying vec3 v_normal;
uniform sampler2D u_planetDay;
uniform sampler2D u_noise; //noise texture (not used yet)
uniform float u_time;
#include simplex_noise_source from Ashima
float noise(vec3 position, int octaves, float frequency, float persistence) {
float total = 0.0; // Total value so far
float maxAmplitude = 0.0; // Accumulates highest theoretical amplitude
float amplitude = 1.0;
for (int i = 0; i < octaves; i++) {
// Get the noise sample
total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
//I USE LINE BELOW FOR 2D NOISE
total += ((1.0 - abs(snoise(position.xy * frequency))) * 2.0 - 1.0) * amplitude;
// Make the wavelength twice as small
frequency *= 2.0;
// Add to our maximum possible amplitude
maxAmplitude += amplitude;
// Reduce amplitude according to persistence for the next octave
amplitude *= persistence;
}
// Scale the result by the maximum amplitude
return total / maxAmplitude;
}
void main()
{
vec3 position = v_normal *2.5+ vec3(u_time, u_time, u_time);
float n1 = noise(position.xyz, 2, 7.7, 0.75) * 0.001;
vec3 ground = texture2D(u_planetDay, v_texCoord+n1).rgb;
gl_FragColor = vec4 (color, 1.0);
}
我如何纠正这个着色器与噪声纹理工作,纹理应该是什么样子?
据我所知,OpenGL ES 2.0
不支持3D纹理。此外,我不知道如何创建3D纹理
我从2D纹理函数中编写了这个3D噪声。x
/y
方向仍采用硬件插值,z
方向手动插值。为了获得z
方向的噪点,我在不同的偏移量下采样了相同的纹理。这可能会导致一些重复,但我在我的应用程序中没有注意到任何重复,我的猜测是使用质数有帮助。
让我在shadertoy.com上难住了一段时间的是,纹理mipmapping被启用了,这导致了floor()
函数值的变化。一个快速的解决方案是将-999
偏置传递给texture2D
。
这是硬编码的256x256噪声纹理,所以相应地调整。
float noise3D(vec3 p)
{
p.z = fract(p.z)*256.0;
float iz = floor(p.z);
float fz = fract(p.z);
vec2 a_off = vec2(23.0, 29.0)*(iz)/256.0;
vec2 b_off = vec2(23.0, 29.0)*(iz+1.0)/256.0;
float a = texture2D(iChannel0, p.xy + a_off, -999.0).r;
float b = texture2D(iChannel0, p.xy + b_off, -999.0).r;
return mix(a, b, fz);
}
更新:为了扩展到柏林噪声,对不同频率的样本求和:
float perlinNoise3D(vec3 p)
{
float x = 0.0;
for (float i = 0.0; i < 6.0; i += 1.0)
x += noise3D(p * pow(2.0, i)) * pow(0.5, i);
return x;
}
尝试在运行时评估噪声通常是一个不好的做法,除非你想做一些研究工作或快速检查/调试你的噪声函数(或看看你的噪声参数在视觉上是什么样子)。
它总是会消耗太多的处理预算(根本不值得),所以忘记在运行时评估噪声。
如果你离线存储你的噪音结果,你将减少充电(超过95%)到一个简单的访问内存。
我建议将所有这些简化为对预烘焙的2D噪点图像进行纹理查找。到目前为止,你只影响碎片管道,所以2D噪声纹理绝对是一种方法(你也可以使用这种2D查找顶点位置变形)。
为了在没有任何连续性问题的情况下将其映射到球体上,您可以生成带有4D噪声的可循环2D图像,为函数提供两个2D圆的坐标。
至于动画,有各种各样的技巧,要么通过在片段管道中使用时间语义来变形你的查找结果,要么在你真的需要噪音的情况下烘一个图像序列"用噪音动画"。
3D纹理只是2D纹理的堆叠,所以它们对于你想要做的操作来说太重了(即使没有动画),而且因为你显然只需要一个体面的太阳表面,这将是过度的。