利用随机噪声生成纹理 Texture Generation using Random Noise A D V E R T I S E M E N T Introduction In n

Texture Generation using Random Noise



A D V E R T I S E M E N T

Introduction


In nature, everything has a random look, while mathematical formulas typically don't generate random looking results, unless you use them well. Random noise, such asPerlin noise invented by Ken Perlin, uses random numbers to generate natural looking textures.

Smooth Noise



As source for the random noise we need an array of random values, called noise[x][y]. Since our interest is generating 2D textures, a 2D array is used. The function generateNoise will fill the array with noise, and the main function is programmed to show this noise array on the screen. The noise itself is generated with the rand() function from the <cstdlib> header file, this function returns a random integer value between 0 and 32768 (as defined in the header file). It's normalized to a random real number between 0 and 1 by dividing it through 32768.0 (make sure to use floating point division).

#define noiseWidth 128
#define noiseHeight 128

double noise[noiseWidth][noiseHeight]; //the noise array

void generateNoise();

int main(int argc, char *argv[])
{
    screen(noiseWidth, noiseHeight, 0, "Random Noise");
    generateNoise();
    
    ColorRGB color;
     
    for(int x = 0; x < w; x++)
    for(int y = 0; y < h; y++)
    {     
        color.r = color.g = color.b = Uint8(256 * noise[x][y]);
        pset(x, y, color);
    }  
    
    redraw();
    sleep();
    return 0;
}

void generateNoise()
{
    for (int x = 0; x < noiseWidth; x++)
    for (int y = 0; y < noiseHeight; y++)
    {
        noise[x][y] = (rand() % 32768) / 32768.0;
    }
}

Here's the noise it generates:



This noise doesn't look very natural however, especially if you zoom in. Zoom in by dividing the x and y used to call the noise array through 8, in the pixel loop of the main function. You get something blocky:


		color.r = color.g = color.b = Uint8(256 * noise[x / 8][y / 8]);
        pset(x, y, color);

When zooming in, we want something smoother. For that, linear interpolation can be used. Currently the noise is an array and it's got only a discrete set of integer indices pointing to it's contents. By using bilinear interpolation on the fractional part, you can make it smoother. For that, a new function, smoothNoise, is introduced:

double smoothNoise(double x, double y)
{  
   //get fractional part of x and y
   double fractX = x - int(x);
   double fractY = y - int(y);
   
   //wrap around
   int x1 = (int(x) + noiseWidth) % noiseWidth;
   int y1 = (int(y) + noiseHeight) % noiseHeight;
   
   //neighbor values
   int x2 = (x1 + noiseWidth - 1) % noiseWidth;
   int y2 = (y1 + noiseHeight - 1) % noiseHeight;

   //smooth the noise with bilinear interpolation
   double value = 0.0;
   value += fractX       * fractY       * noise[x1][y1];
   value += fractX       * (1 - fractY) * noise[x1][y2];
   value += (1 - fractX) * fractY       * noise[x2][y1];
   value += (1 - fractX) * (1 - fractY) * noise[x2][y2];

   return value;
}

The returned value is the weighed average of 4 neighboring pixels of the array. In the main function, now use this instead of directly calling the noise array, and use real numbers for the division:

 
		color.r = color.g = color.b = Uint8(256 * smoothNoise(x / 8.0, y / 8.0));
        pset(x, y, color);

This is again the result zoomed in 8 times, but now with the bilinear interpolation. If you don't zoom in you won't be able to see the interpolation:



This is quite useful for random noise, the smoothing method could be better maybe, bilinear interpolation is often used by 3D cards for smoothing textures in games as a cheap and fast technique.

Let's call this image a "noise texture".

Turbulence


Turbulence is what creates natural looking features out of smoothed noise. The trick is to add multiple noise textures of different zooming scales together. An example of how this represents nature can be found in a mountain range: there are very large features (the main mountains), they are very deeply zoomed in noise.



Then added to the mountains are smaller features: multiple tops, variations in the slope, ...



Then, at an even smaller scale, there are rocks on the mountains.



An even smaller layer is the grains of sand. Together, the sum of all these layers forms natural looking mountains.

In 2D, this is done by adding different sizes of the smoothed noise together.



The zooming factor started at 16 here, and is divided through two each time. Keep doing this until the zooming factor is 1. The small features in the mountain example weren't only smaller in the width, but also in the height. To achieve this in 2D textures, make the images with a smaller zoom darker, so adding them will have less effect:



By adding these 5 images together, and dividing the result through 5 to get the average, you get a turbulence texture:



Here's a function that'll automaticly do all this for a single pixel. The parameter "size" is the initial zoom factor, which was 16 in the example above. The return value is normalized so that it'll be a number between 0 and 255.

double turbulence(double x, double y, double size)
{
    double value = 0.0, initialSize = size;
    
    while(size >= 1)
    {
        value += smoothNoise(x / size, y / size) * size;
        size /= 2.0;
    }
    
    return(128.0 * value / initialSize);
}

To use the turbulence function, change the small part of the code in the loop that goes through every pixel by the following:

        color.r = color.g = color.b = Uint8(turbulence(x, y, 64));
        pset(x, y, color);

The size is set to 64 there, and the result looks like this:



If you set the initial size to 256 instead, the result is much bigger and smoother:



And here's a very small initial size of only 8:



The textures here have some obvious horizontal and vertical lines because of the bilinear filter smooth function. The Clouds filter in  Photoshop generates a texture similar to the ones above, but with a nicer smooth function. Nicer smooth functions are beyond the scope of this article though.

If you use no smooth function at all, it looks like this:

<think>嗯,用户问的是关于如何使用GL_TEXTURE_EXTERNAL_OES加载Bitmap纹理的问题。首先,我需要回忆一下OpenGL中纹理类型的基本知识。GL_TEXTURE_EXTERNAL_OES通常是用于Android中的SurfaceTexture,这类纹理和普通的2D纹理有什么不同呢? 首先,我记得外部纹理(GL_TEXTURE_EXTERNAL_OES)和普通的GL_TEXTURE_2D在使用上有区别。外部纹理需要特定的扩展,通常在着色器中要声明#extension GL_OES_EGL_image_external : require。这一点用户可能需要注意,否则编译着色器时会报错。 然后,用户提到加载的是Bitmap纹理。一般来说,Bitmap在Android中是通过GLUtils.texImage2D加载到GL_TEXTURE_2D中的。但用户希望使用GL_TEXTURE_EXTERNAL_OES,这可能是因为他们需要用到SurfaceTexture的特性,例如从相机预览或视频中获取帧数据。不过,这里用户的具体需求是加载Bitmap,可能需要不同的处理方式。 接下来,我需要考虑如何将Bitmap转换为外部纹理。常规的做法可能是先创建一个SurfaceTexture,然后将Bitmap绘制到SurfaceTexture关联的Surface上。但具体步骤可能涉及到OpenGL ES的上下文管理,以及如何正确绑定纹理。 可能的步骤包括: 1. 生成一个外部纹理对象,类型设置为GL_TEXTURE_EXTERNAL_OES。 2. 创建SurfaceTexture实例,传入这个纹理对象。 3. 将Bitmap绘制到SurfaceTexture对应的Surface上,可能需要使用Canvas进行绘制。 4. 更新纹理内容,调用SurfaceTexture的updateTexImage()方法。 需要注意的是,使用SurfaceTexture时,纹理的更新是在另一个线程,可能需要同步处理。此外,外部纹理的采样在着色器中需要使用samplerExternalOES类型的采样器,否则会导致采样错误。 另外,用户可能遇到的问题包括纹理不显示、黑屏或者采样错误。这时候需要检查是否正确声明了扩展,是否正确绑定纹理单元,以及是否调用了updateTexImage来更新纹理数据。 总结来说,虽然GL_TEXTURE_EXTERNAL_OES通常用于动态内容,但加载静态Bitmap也是可行的,需要结合SurfaceTexture和Canvas绘制来实现。不过,可能相对于直接使用GL_TEXTURE_2D更复杂一些,需要用户理解其中的步骤和注意事项。</think>好的,我将逐步解释如何使用`GL_TEXTURE_EXTERNAL_OES`加载Bitmap纹理: $$注意:GL_TEXTURE_EXTERNAL_OES\text{是Android中为SurfaceTexture设计的特殊纹理类型,与常规}GL_TEXTURE_2D\text{有本质区别}$$ ### 一、核心原理 1. **纹理类型差异**: - `GL_TEXTURE_EXTERNAL_OES`需要EGLImage支持 - 采样时需在着色器声明:`#extension GL_OES_EGL_image_external : require` - 纹理坐标系为`[0,1)`范围 2. **Bitmap适配问题**: - 原生无法直接通过`glTexImage2D`加载 - 需要借助`SurfaceTexture`机制中转 ### 二、实现步骤 #### 1. 创建外部纹理 ```java int[] textureID = new int[1]; glGenTextures(1, textureID, 0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID[0]); // 设置滤波参数 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ``` #### 2. 构建SurfaceTexture桥梁 ```java SurfaceTexture surfaceTexture = new SurfaceTexture(textureID[0]); Surface surface = new Surface(surfaceTexture); // 将Bitmap绘制到Surface Canvas canvas = surface.lockCanvas(null); canvas.drawBitmap(yourBitmap, 0, 0, null); surface.unlockCanvasAndPost(canvas); // 强制更新纹理数据 surfaceTexture.updateTexImage(); ``` #### 3. 着色器配置 顶点着色器示例: ```glsl attribute vec4 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; void main() { gl_Position = aPosition; vTexCoord = aTexCoord; } ``` 片元着色器**必须**声明: ```glsl #extension GL_OES_EGL_image_external : require precision mediump float; uniform samplerExternalOES uTexture; varying vec2 vTexCoord; void main() { gl_FragColor = texture2D(uTexture, vTexCoord); } ``` ### 三、关键注意事项 1. **纹理绑定同步**: ```java surfaceTexture.updateTexImage(); // 每次渲染前调用 float[] mtx = new float[16]; surfaceTexture.getTransformMatrix(mtx); // 获取纹理矩阵 ``` 2. **生命周期管理**: ```java surface.release(); surfaceTexture.release(); glDeleteTextures(1, textureID, 0); ``` ### 四、典型问题排查 1. **黑屏问题**: - 检查`#extension`声明 - 验证`updateTexImage()`是否调用 - 确认Bitmap成功绘制到Surface 2. **纹理错位**: ```glsl // 在片元着色器中应用变换矩阵 vec2 adjustedCoord = (vec4(vTexCoord,0,1) * mtx).xy; ``` 3. **性能优化**: - 复用SurfaceTexture实例 - 避免频繁创建/销毁对象 - 使用纹理压缩格式(如ETC2) 这种方法虽然比直接使用`GL_TEXTURE_2D`复杂,但支持后续动态更新纹理内容,适用于需要实时更新图像源的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值