Hello again,
I finally managed to find the time to implement my film grain effect as a separate RenderPass. It scales to any screen size and performance is good, too. You'll find a screen shot attached to this post.
One problem remains: The intensity of the texture (the alpha value, if you want) is currently hard-coded into NoiseTexture3D and has to be set before the Noise Texture is generated. It would be nice to be able to upgrade this during runtime.
Question
How can i set the Alphavalue of a Texture during run time?
Here is the source:
NoiseTexture3D
import java.nio.ByteBuffer;
import java.util.Random;
import com.jme.image.Image;
import com.jme.image.Texture;
/**
* <code>NoiseTexture3D</code> creates and holds a threedimensional noise Texture
* filled with value noise.
*
* @author schubj
*
*/
public class NoiseTexture3D {
private Texture[] noise3D;
private Random rng;
private double intensity;
/**
*
* @return The Noise intensity.
*/
public double getIntensity() {
return intensity;
}
/**
* Set the intensity of Noise to a value between 0.0 (invisible) to 1.0 (fully visible).
* Default value is 0.3. You can only set the intensity before you use <code>NoiseTexture3D.generate(int width, int height)</code>
*/
public void setIntensity(double intensity) {
if(intensity > 1.0) {
intensity = 1.0;
} else if (intensity < 0.0) {
intensity = 0.0;
}
this.intensity = intensity;
}
/** <code>NoiseTexture3D</code> creates and holds a threedimensional noise Texture
* filled with value noise.
*
* @param depth The number of textures available. The more textures you set, the more
* variety you get.
*/
public NoiseTexture3D(int depth) {
noise3D = new Texture[depth];
this.intensity = 0.3;
rng = new Random();
}
/**
* Creates the threedimensional Noise Texture. Should be called after intensity is set.
* @param width Width of the Texture. Should be a power of two (32,64,128)
* @param height Height of the Texture. Should be a power of two (32,64,128)
*/
public void generate(int width, int height) {
for(int i = 0; i < noise3D.length; i++) {
noise3D[i] = generateNoiseTexture(width, height);
}
}
/**
*
* @return The texture to be used by a texture state to be displayed.
*/
public Texture get() {
int pos = rng.nextInt(noise3D.length);
return noise3D[pos];
}
/**
* Generates one Texture filled with value noise. Only for internal use.
*/
private Texture generateNoiseTexture(int width, int height) {
ByteBuffer data = ByteBuffer.allocate(width*height*3);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
byte pixel = (byte) (int) ((intensity*255) * rng.nextFloat());
data.put(pixel);
data.put(pixel);
data.put(pixel);
}
}
Image image = new Image(1,width,height,data);
Texture texture = new Texture();
texture.setFilter( Texture.FM_LINEAR );
texture.setWrap(Texture.WM_WRAP_S_WRAP_T);
texture.setImage(image);
return texture;
}
}
NoiseRenderPass
package viset.shaders.renderpasses;
import java.nio.FloatBuffer;
import com.jme.image.Texture;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
/**
* <code>NoiseRenderPass</code> is a pass that Renders a transparent Noise Texture over normal
* geometry. It can be used in any Pass Game. The Texture automatically tiles to accomodate the whole screen.
* It should be added last to the <code>PassManager</code>.
*
* @author schubj
*
*/
public class NoiseRenderPass extends Pass {
private NoiseTexture3D noise3d;
private TextureState ts;
private Quad fullScreenQuad;
/**
*
* @return The Noise intensity.
*/
public double getIntensity() {
return noise3d.getIntensity();
}
/**
* Set the intensity of Noise to a value between 0.0 (invisible) to 1.0 (fully visible).
* Default value is 0.3.
*/
public void setIntensity(double intensity) {
if(intensity > 1.0) {
intensity = 1.0;
} else if (intensity < 0.0) {
intensity = 0.0;
}
noise3d.setIntensity(intensity);
}
/**
* <code>NoiseRenderPass</code> is a pass that Renders a transparent Noise Texture over normal
* geometry. It can be used in any Pass Game.
*
* It should be added last to the <code>PassManager</code>
*
* @author schubj
*
*/
public NoiseRenderPass() {
DisplaySystem display = DisplaySystem.getDisplaySystem();
//Texture State
ts = display.getRenderer().createTextureState();
//Alpha State
AlphaState as1 = display.getRenderer().createAlphaState();
as1.setBlendEnabled(true);
as1.setSrcFunction(AlphaState.SB_SRC_ALPHA);
as1.setDstFunction(AlphaState.DB_ONE);
as1.setTestEnabled(true);
as1.setTestFunction(AlphaState.TF_GREATER);
as1.setEnabled(true);
//Generate Texture
noise3d = new NoiseTexture3D(64);
noise3d.setIntensity(0.2);
noise3d.generate(512, 512);
ts.setTexture(noise3d.get());
//Create fullscreen quad
fullScreenQuad = new Quad("FullScreenQuad", display.getWidth(), display.getHeight());
fullScreenQuad.setLocalTranslation(display.getWidth() / 2, display.getHeight() / 2, 0);
fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);
fullScreenQuad.setTextureCombineMode(TextureState.REPLACE);
fullScreenQuad.setLightCombineMode(LightState.OFF);
fullScreenQuad.setRenderState(ts);
fullScreenQuad.setRenderState(as1);
//Tile the texture
int tilingvalue = display.getWidth() / 512 + 1;
FloatBuffer tBuf = fullScreenQuad.getTextureBuffer(0, 0);
tBuf.clear();
tBuf.put(0).put(tilingvalue);
tBuf.put(0).put(0);
tBuf.put(tilingvalue).put(0);
tBuf.put(tilingvalue).put(tilingvalue);
}
/**
* Fetch a new Noise Texture from the <code>NoiseTexture3d</code>. Texture Pool, and
* render the Noise. Is called automatically.
*/
protected void doRender(Renderer r) {
ts.setTexture(noise3d.get());
System.out.println(ts.isEnabled());
fullScreenQuad.setRenderState(ts);
fullScreenQuad.updateRenderState();
r.draw(fullScreenQuad);
}
}
TestNoise
package viset.shaders;
import viset.shaders.renderpasses.NoiseRenderPass;
import com.jme.app.SimpleGame;
import com.jme.app.SimplePassGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Torus;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
/**
* Demonstrates using the <code>NoiseRenderPass</code>
*
* @author schubj
*/
public class TestNoise extends SimplePassGame
{
private NoiseRenderPass noiseRenderPass;
public static void main(String[] args)
{
TestNoise app = new TestNoise();
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
/**
* The RenderPasses have to be set up here.
*
* @see com.jme.app.BaseSimpleGame#simpleInitGame()
*/
protected void simpleInitGame()
{
//Setup camera
cam.setFrustumPerspective(55.0f, (float) display.getWidth() / (float) display.getHeight(), 1, 1000);
//Setup lights
PointLight light = new PointLight();
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
light.setLocation(new Vector3f(0, 30, 0));
light.setEnabled(true);
lightState.attach(light);
rootNode.attachChild(createObjects());
//Setup Renderpasses
RenderPass rootPass = new RenderPass();
rootPass.add(rootNode);
pManager.add(rootPass);
noiseRenderPass = new NoiseRenderPass();
pManager.add(noiseRenderPass);
}
protected void simpleUpdate() {
}
/**
* Creates a bunch of test objects.
*
* @return Node holding all the objects
*/
private Node createObjects() {
Node objects = new Node("objects");
Torus torus = new Torus("Torus", 50, 50, 10, 20);
torus.setLocalTranslation(new Vector3f(50, -5, 20));
TextureState ts = display.getRenderer().createTextureState();
Texture t0 = TextureManager.loadTexture(
TestBloom.class.getClassLoader().getResource(
"jmetest/data/images/Monkey.jpg"),
Texture.MM_LINEAR_LINEAR,
Texture.FM_LINEAR);
Texture t1 = TextureManager.loadTexture(
TestBloom.class.getClassLoader().getResource(
"jmetest/data/texture/north.jpg"),
Texture.MM_LINEAR_LINEAR,
Texture.FM_LINEAR);
t1.setEnvironmentalMapMode(Texture.EM_SPHERE);
ts.setTexture(t0, 0);
ts.setTexture(t1, 1);
ts.setEnabled(true);
torus.setRenderState(ts);
objects.attachChild(torus);
ts = display.getRenderer().createTextureState();
t0 = TextureManager.loadTexture(
TestBloom.class.getClassLoader().getResource(
"jmetest/data/texture/wall.jpg"),
Texture.MM_LINEAR_LINEAR,
Texture.FM_LINEAR);
t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
ts.setTexture(t0);
Box box = new Box("box1", new Vector3f(-10, -10, -10), new Vector3f(10, 10, 10));
box.setLocalTranslation(new Vector3f(0, -7, 0));
box.setRenderState(ts);
objects.attachChild(box);
box = new Box("box2", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
box.setLocalTranslation(new Vector3f(15, 10, 0));
box.setRenderState(ts);
objects.attachChild(box);
box = new Box("box3", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
box.setLocalTranslation(new Vector3f(0, -10, 15));
box.setRenderState(ts);
objects.attachChild(box);
box = new Box("box4", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
box.setLocalTranslation(new Vector3f(20, 0, 0));
box.setRenderState(ts);
objects.attachChild(box);
box = new Box("box5", new Vector3f(-50, -2, -50), new Vector3f(50, 2, 50));
box.setLocalTranslation(new Vector3f(0, -15, 0));
box.setRenderState(ts);
box.setModelBound(new BoundingBox());
box.updateModelBound();
objects.attachChild(box);
ts = display.getRenderer().createTextureState();
t0 = TextureManager.loadTexture(
TestBloom.class.getClassLoader().getResource(
"jmetest/data/texture/cloud_land.jpg"),
Texture.MM_LINEAR_LINEAR,
Texture.FM_LINEAR);
t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
ts.setTexture(t0);
box = new Box("floor", new Vector3f(-1000, -10, -1000), new Vector3f(1000, 10, 1000));
box.setLocalTranslation(new Vector3f(0, -100, 0));
box.setRenderState(ts);
box.setModelBound(new BoundingBox());
box.updateModelBound();
objects.attachChild(box);
return objects;
}
}