package org.osm2world.core.target.jogl;
import static javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT;
import static javax.media.opengl.GL.GL_DEPTH_TEST;
import javax.media.opengl.GL;
import javax.media.opengl.GL3;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.material.Material.AmbientOcclusion;
/**
* Shader to render the depth buffer into a texture that can be used to implement SSAO later.
*/
public class SSAOShader extends DepthBufferShader {
private int depthBufferHandle;
private int frameBufferHandle;
private int width;
private int height;
public SSAOShader(GL3 gl) {
super(gl);
initialize();
}
/**
* Setup the framebuffer and texture.
*/
private void initialize() {
int[] viewport = new int[4];
gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
width = viewport[2];
height = viewport[3];
// create the shadow map texture / depth buffer
int[] tmp = new int[1];
gl.glGenTextures(1,tmp,0);
depthBufferHandle = tmp[0];
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, depthBufferHandle);
gl.glTexImage2D(GL.GL_TEXTURE_2D, // target texture type
0, // mipmap LOD level
GL3.GL_DEPTH_COMPONENT, // internal pixel format
//GL_DEPTH_COMPONENT
width, // width of generated image
height, // height of generated image
0, // border of image
GL3.GL_DEPTH_COMPONENT, // external pixel format
GL.GL_UNSIGNED_BYTE, // datatype for each value
null); // buffer to store the texture in memory
// some settings for the shadow map texture
// GL_LINEAR might produce better results, but is slower. GL_NEAREST shows aliasing artifacts clearly
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
/* For texture access outside the shadow map use the highest depth value possible (1.0).
* This means the fragment lies outside of the lights frustum and no shadow should be applied.
* Therefore we use CLAMP_TO_BORDER with a border of (1.0, 0.0, 0.0, 0.0)
*/
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
//float [] border = {1.0f, 0.0f, 0.0f, 0.0f};
//gl.glTexParameterfv(GL.GL_TEXTURE_2D, GL3.GL_TEXTURE_BORDER_COLOR, border, 0);
/* special for depth textures: do not retrieve the texture values, but the result of a comparison.
* compare the third value (r) of the texture coordinate against the depth value stored at the texture coordinate (s,t)
* result will be 1.0 if r is less than the texture value (which means the fragment is nearer) and 0.0 otherwise
*/
//gl.glTexParameteri(GL.GL_TEXTURE_2D, GL3.GL_TEXTURE_COMPARE_MODE, GL3.GL_COMPARE_REF_TO_TEXTURE);
//gl.glTexParameteri(GL.GL_TEXTURE_2D, GL3.GL_TEXTURE_COMPARE_FUNC, GL.GL_LESS);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, depthBufferHandle);
/*gl.glGenTextures(1,tmp,0);
colorBufferHandle = tmp[0];
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, colorBufferHandle);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexImage2D(GL.GL_TEXTURE_2D, // target texture type
0, // mipmap LOD level
GL.GL_RGBA, // internal pixel format
//GL_DEPTH_COMPONENT
shadowMapWidth, // width of generated image
shadowMapHeight, // height of generated image
0, // border of image
GL.GL_RGBA, // external pixel format
GL.GL_UNSIGNED_BYTE, // datatype for each value
null); // buffer to store the texture in memory
*/
// create the frame buffer object (FBO)
gl.glGenFramebuffers(1, tmp, 0);
frameBufferHandle = tmp[0];
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBufferHandle);
//Attach 2D texture to this FBO
gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
GL.GL_TEXTURE_2D,
depthBufferHandle,0);
/*gl.glFramebufferTexture(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
depthBufferHandle,0);*/
/*gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
GL.GL_COLOR_ATTACHMENT0,
GL.GL_TEXTURE_2D,
colorBufferHandle,0);*/
// set target for fragment shader output: not used, we only need the depth buffer
//int[] drawBuffers = {GL.GL_NONE};
//gl.glDrawBuffers(1, drawBuffers, 0);
//gl.glDrawBuffer(GL.GL_COLOR_ATTACHMENT0);
gl.glDrawBuffer(GL.GL_NONE);
gl.glReadBuffer(GL.GL_NONE);
//gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
//Disable color buffer
//http://stackoverflow.com/questions/12546368/render-the-depth-buffer-into-a-texture-using-a-frame-buffer
//gl.glDrawBuffer(GL2.GL_NONE);
//gl.glReadBuffer(GL2.GL_NONE);
//Set pixels ((width*2)* (height*2))
//It has to have twice the size of shadowmap size
//pixels = GLBuffers.newDirectByteBuffer(shadowMapWidth*shadowMapHeight*4);
//Set default frame buffer before doing the check
//http://www.opengl.org/wiki/FBO#Completeness_Rules
//gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER);
// Always check that our framebuffer is ok
if(gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER) != GL.GL_FRAMEBUFFER_COMPLETE)
{
throw new RuntimeException("Can not use FBO! Status error:" + status);
}
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
}
/**
* Resize the framebuffer backing texture, if size doesn't match.
*/
private void resizeBuffer(int width, int height) {
if (width != this.width || height != this.height) {
this.width = width;
this.height = height;
gl.glBindTexture(GL.GL_TEXTURE_2D, depthBufferHandle);
gl.glTexImage2D(GL.GL_TEXTURE_2D, // target texture type
0, // mipmap LOD level
GL3.GL_DEPTH_COMPONENT, // internal pixel format
//GL_DEPTH_COMPONENT
width, // width of generated image
height, // height of generated image
0, // border of image
GL3.GL_DEPTH_COMPONENT, // external pixel format
GL.GL_UNSIGNED_BYTE, // datatype for each value
null); // buffer to store the texture in memory
}
}
/**
* Prepares rendering of the depth map. Binds the framebuffer and clears it.
* The size of the framebuffer is automatically adjusted to match the current viewport.
*/
private void prepareDepthMapGeneration() {
int[] viewport = new int[4];
gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
resizeBuffer(viewport[2], viewport[3]);
// bind FBO
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBufferHandle);
// clear shadow map
gl.glClear(GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL_DEPTH_TEST);
}
/**
* {@inheritDoc}
* Only primitives that support ambient occlusion will get rendered.
*/
@Override
public boolean setMaterial(Material material, JOGLTextureManager textureManager) {
if (material.getAmbientOcclusion() == AmbientOcclusion.FALSE) {
return false;
}
return super.setMaterial(material, textureManager);
}
/**
* Returns the handle of the texture containing the rendered depth map.
*/
public int getDepthBuferHandle() {
return depthBufferHandle;
}
/**
* Prepares rendering of the depth map. This changes the current framebuffer.
* {@link #disableShader()} should be called after the rendering is complete to bind the default framebuffer again.
*/
@Override
public void useShader() {
super.useShader();
prepareDepthMapGeneration();
}
/**
* Completes the rendering of the depth map. The default framebuffer gets restored.
*/
@Override
public void disableShader() {
//ShaderManager.saveDepthBuffer(new File("/home/sebastian/ssao_depth.png"), depthBufferHandle, width, height, gl);
// bind default framebuffer
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
super.disableShader();
}
}