I was reading the orange book, and they had a shader which I couldn’t find implemented in jME anywhere, its called Texture or Glyph Bombing. You can read more about it here:
http://http.developer.nvidia.com/GPUGems/gpugems_ch20.html
In short you create a texture atlas of images, and then just bombard a model with them in a random arrangement. But really your model is divided into cells, and then a random image is populated there. You can add scale/rotation if you wish, and even animations. Apparently its useful for repeated stuff like wallpaper and dirt. The downside is that it requires quite a lot of texture lookups. Anyways heres a video:
[video]http://www.youtube.com/watch?v=NYVA5k610Mc[/video]
And heres the code:
Testcase:
[java]package com.mmm.util.test;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
public class TestTextureBombing extends SimpleApplication {
public static void main(String[] args) {
new TestTextureBombing().start();
}
@Override
public void simpleInitApp() {
Box b = new Box(1, 1, 1);
Spatial spatial = new Geometry("box", b);
Material textureBombMat = new Material(assetManager, "MatDefs/TextureBomb.j3md");
textureBombMat.setTexture("TextureAtlas", assetManager.loadTexture("Textures/Glyphs.png"));
textureBombMat.setTexture("NoiseTex", assetManager.loadTexture("Textures/Noise.png"));
// unshadedMat.setColor("Color", new ColorRGBA(0, 1.0f, 1.0f, 1.0f));
// unshadedMat.setTexture("ColorMap", assetManager.loadTexture("Textures/ColorMap.png"));
textureBombMat.setFloat("ScaleFactor", 10f); // Scales the tex coords by 10, increasing the cell count
textureBombMat.setFloat("NumImages", 10f); // A 10 x 10 texture atlas (100 images)
textureBombMat.setFloat("Percentage", 0.9f); // 90% of cells will be filled
textureBombMat.setBoolean("RandomScale", true); // Should the images be randomly scaled?
textureBombMat.setBoolean("RandomRotate", true); // Should they be randomly rotated?
textureBombMat.setFloat("SamplesPerCell", 1.0f); // Should be >= 1.0f (How many images per cell)
textureBombMat.setBoolean("Animated", false); // Should the images be animated?
// Change filters otherwise there are artifacts
textureBombMat.getTextureParam("TextureAtlas").getTextureValue().setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
textureBombMat.getTextureParam("NoiseTex").getTextureValue().setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
spatial.setMaterial(textureBombMat);
rootNode.attachChild(spatial);
}
}[/java]
TextureBomb.vert:
[java]
attribute vec3 inPosition;
attribute vec2 inTexCoord;
uniform mat4 g_WorldViewProjectionMatrix;
varying vec2 texCoord;
void main() {
gl_Position = g_WorldViewProjectionMatrix * vec4 (inPosition, 1.0);
texCoord = inTexCoord.xy;
}[/java]
TextureBomb.frag
[java]
#define TWO_PI 6.28318
#ifdef HAS_COLOR
uniform vec4 m_Color;
#endif
#ifdef HAS_COLORMAP
uniform sampler2D m_ColorMap;
#endif
uniform sampler2D m_TextureAtlas;
uniform sampler2D m_NoiseTex;
uniform float g_Time;
uniform float m_ScaleFactor;
uniform float m_Percentage;
uniform float m_SamplesPerCell;
uniform float m_RO1;
uniform float m_NumImages;
uniform bool m_RandomScale;
uniform bool m_RandomRotate;
uniform bool m_Animated;
varying vec2 texCoord;
void main()
{
vec4 color = vec4(1.0);
#ifdef HAS_COLORMAP
color *= texture2D (m_ColorMap, texCoord);
#endif
#ifdef HAS_COLOR
color *= m_Color;
#endif
texCoord *= m_ScaleFactor;
vec2 cell = floor(texCoord);
vec2 offset = texCoord - cell;
for (int i = -1; i <= int (m_RandomRotate); i++) {
for (int j = -1; j <= int (m_RandomRotate); j++) {
vec2 currentCell = cell + vec2(float(i), float(j));
vec2 currentOffset = offset - vec2(float(i), float(j));
vec2 randomUV = currentCell * vec2(m_RO1);
for (int k = 0; k < int (m_SamplesPerCell); k++) {
vec4 random = texture2D(m_NoiseTex, randomUV * (m_Animated ? g_Time * 0.05 : 1.0));
randomUV += random.ba;
if (random.r < m_Percentage) {
vec2 glyphIndex;
mat2 rotator;
vec2 index;
float rotationAngle, cosRot, sinRot;
index.s = floor(random.b * m_NumImages);
index.t = floor(random.g * m_NumImages);
if (m_RandomRotate) {
rotationAngle = TWO_PI * random.g;
cosRot = cos(rotationAngle);
sinRot = sin(rotationAngle);
rotator[0] = vec2(cosRot, sinRot);
rotator[1] = vec2(-sinRot, cosRot);
glyphIndex = -rotator * (currentOffset - random.rg);
}
else {
glyphIndex = currentOffset - random.rg;
}
if (m_RandomScale) {
glyphIndex /= vec2(0.5 * random.r + 0.5);
}
glyphIndex = (clamp(glyphIndex, 0.0, 1.0) + index) * (1 / m_NumImages);
vec4 image = texture2D(m_TextureAtlas, glyphIndex);
if (image.r != 1.0) {
color.rgb = mix(random.rgb * 0.7, color.rgb, image.r);
}
}
}
}
}
gl_FragColor = color;
}
[/java]
TextureBomb.j3md
[java]
MaterialDef TextureBomb {
MaterialParameters {
Color Color
Texture2D ColorMap
Texture2D TextureAtlas
Texture2D NoiseTex
Float ScaleFactor
Float Percentage : 0.5
Float SamplesPerCell : 1.0
Float RO1 : 0.01
Float NumImages
Boolean Animated : False
Boolean RandomScale : False
Boolean RandomRotate : False
}
Technique {
VertexShader GLSL100: Shaders/TextureBomb.vert
FragmentShader GLSL100: Shaders/TextureBomb.frag
WorldParameters {
WorldViewProjectionMatrix
Time
}
Defines {
HAS_COLORMAP : ColorMap
HAS_COLOR : Color
}
}
}
[/java]