GLSL - 使用2D纹理进行3D Perlin噪声而不是程序性3D噪声

时间:2015-06-02 12:26:39

标签: glsl opengl-es-2.0 texture-mapping perlin-noise simplex-noise

我为太阳表面实现了一个着色器,它使用来自ashima/webgl-noisesimplex噪音。但它耗费了太多的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纹理。

2 个答案:

答案 0 :(得分:1)

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

更新:要扩展到perlin噪声,请对不同频率的样本求和:

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

答案 1 :(得分:1)

尝试在运行时评估噪声通常是一种不好的做法,除非您想要进行一些研究工作或快速检查/调试您的噪声功能(或查看您的噪声参数在视觉上看起来像)。

总是会消耗太多的处理预算(根本不值得),所以忘记在运行时评估噪音。

如果您将噪音结果存储在离线状态,您将减少费用(例如超过95%)以便简单地访问内存。

我建议将所有这些减少到预先烘焙的2D噪声图像上的纹理查找。到目前为止,您只影响片段管道,因此2D噪声纹理绝对是可行的方法(您也可以使用此2D查找来进行顶点位置变形)。

为了将其映射到球体上而没有任何连续性问题,您可以生成具有4D噪声的可循环2D图像,使用两个2D圆的坐标来提供该函数。

至于动画制作,有各种各样的hackish技巧,要么通过片段管道中的时间语义来改变你的查找结果,要么在你真的需要噪音的情况下烘焙图像序列&#34;用噪音动画#34;。

3D纹理只是2D纹理的堆叠,所以它们太重而无法操作(即使没有动画)你想要做的事情,而且因为你显然只需要一个像样的太阳表面,所以它会有点过分。