# OpenGL (JOGL) - Radial Blur Effekt (Glow)



## jaH.da.izi (31. Aug 2008)

Moin erstmal.

Also ich bastel jetzt schon seit einer ganzen Weile mit JOGL ein kleines 3d-Universum. Dieses Universum ernhält Sonnen, Planeten und Monde. Die Objekte bewegen sich auf ihren Kreisbahnen, rotieren, etc.

Wenn ich jetzt eine Sonne erstelle, dann möchte ich in der Mitte der Sonne eine Lichtquelle positionieren. Das Problem ist jetzt aber, dass die Sonne selbst von der Lichtquelle in ihrem Mittelpunkt nicht angestrahlt wird. Ausserdem entsteht bei einer einfachen Lichtquelle kein schöner "Glüheffekt".

Deshalb dachte ich mir, ich versuche mich mit dem Radial Blur Effekt (wie aus dem Nehe-Tutorial: http://www.java-tips.org/other-api-...endering-to-a-texture-nehe-tutorial-jogl.html). Das ganze sieht auch schon super aus, aber eben auch nur dann, wenn "alles" mittig positioniert wird. Ich weiss nicht genau, wie ich das erklären soll, aber ich versuche es mal bildlich:
Wenn ich die Sonne einfach mal mittig positioniere, also den Mittelpunkt bei 0,0,0 setze, dann habe ich ein kleines Problem, wenn ICH mich im Raum bewege (glulookat). Also angenommen die Sonne ist bei 0,0,0 positioniert, ich stehe auf der Z-Achse und schaue nach 0,0,0. Wenn ich mich jetzt zum Beispiel auf der X-Achse nach links (negative Richtung) bewege, dann verschiebt sich mein Blur-Effekt etwas. Das ganze sieht dann mehr aus wie ein Lichtstrahl und nicht wie ein gleichmäßiger Gloweffekt.

Meine Sonne soll also immer gleich aussehen, egal wo ich mich befinde. Sie soll quasi immer in alle Richtungen Strahlen.

Und genau das ist das Problem, ich kriege es irgendwie nicht hin, den Blureffekt beliebig zu positionieren. Doch, das schon, aber der Effekt verschiebt sich eben etwas.



Hier mal mittig positioniert:





Und hier habe ich das ganze (also die glutSolidSphere) mit glTranslatef auf der X-Achse um 150 nach rechts verschoben:









Hat jemand eine Idee, wie ich das Problem lösen könnte? Ich habe mich bei meinem Blur sehr an das Nehe-Tutorial gehalten und eben auch diesen Code für den Blur verwendet:





```
gl.glBegin(GL.GL_QUADS);
       for (int num = 0; num < times; num++)  // Anzahl, wie oft Blur gerendert wird
          
       {
           gl.glColor4f(1.0f, 1.0f, 1.0f, alpha);
           
           gl.glTexCoord2f(0,1);
           gl.glVertex2f(-800*spost,-800*spost);
           gl.glTexCoord2f(0,0);
           gl.glVertex2f(-800*spost,800+800*spost);
           gl.glTexCoord2f(1,0);
           gl.glVertex2f(800+800*spost,800+800*spost);
           gl.glTexCoord2f(1,1);
           gl.glVertex2f(800+800*spost,-800*spost);


           spost = spost + inc;
           alpha = alpha - alphainc;
       }
       gl.glEnd();
```



(Weiss zwar momentan nichtmehr genau, woher ich diese Berechnungen habe, aber sie funktionieren ;-). Mein Fenster ist übrigens 800x800 groß.)


Wenn ich in der Schleife die Werte des ersten glVertex2f Aufrufs ändere in gl.glVertex2f(-1800*spost,-800*spost) (einfach probiert), dann komme ich meinem gewünschten Ziel komischerweise recht nah. Seht selbst:







D.h. das ganze hat irgendwie was mit der Positionierung innerhalb der Schleife zu tun, denke ich. Kurz gesagt: Wenn ich mich im Raum bewege, dann muss die Position des Blurs irgendwie angepasst werden. 


Ich wäre wirklich für jede Hilfe sehr dankbar, sitze schon sehr lange an diesem Effekt und irgendwie möchte ich diesen, bevor ich im Universum weitermache, fertiggestellt haben.
Auch Beispielcode in C++/C kann helfen, ist egal, da wühle ich mich schon irgendwie durch. 



Ich hoffe ihr habt einigermaßen verstanden, was ich für ein Problem habe. )





Oder könnte Radial-Blur doch die falsche Richtung sein? Ich lasse mich gerne eines besseren belehren.[/code]


----------



## Fancy (1. Sep 2008)

Moin,

der im NeHe-Tutorial gezeigte Effekt entsteht indem mehrfach in die glow Textur hinein skaliert wird. In Deinem gezeigten Code skalierst Du immer zur Mitte der Textur, wobei Du über die Position der Vertices, das original Tutorial über die Texturkoordinaten, skalierst. 

Dabei bleibt der Effekt gleich, alle Objekte in der glow Textur bekommen eine Corona und zwar ausgeht vom Skalierungszentrum der Textur. Befindet sich Deine Sonne nun nicht im Zentrum der Szene, entsteht der von Dir dargestellte Effekt.

Um das auszugleichen darfst Du nicht in das Zentrum der Textur skalieren, sondern zur aktuellen Position der Sonne innerhalb der Textur. Dazu musst Du in den einzelnen Renderphasen die aktuellen Transformationsmatrizen sichern, damit Du von Deinen 3D Koordinaten im Objektraum auf die 2D Koordinaten der Textur kommst. Mit diesen 2D Koordinaten kannst Du dann die Textur entsprechend skalieren.

Z.B. in etwa so:


```
package fancy.jf.glow;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import com.sun.opengl.util.Animator;

public class GlowFF implements GLEventListener {

    private static final int     sunGlowTextureSize = 128;
    private static final float[] sunPosition        = { 0.0f, 0.0f, 0.0f };

    private GL                   gl                 = null;
    private GLU                  glu                = null;

    private int                  fboHandle          = 0;
    private int                  rboHandle          = 0;
    private int                  glowHandle         = 0;

    private double[]             modelview          = null;
    private double[]             projection         = null;
    private int[]                viewport           = null;

    
    @Override
    public void init(GLAutoDrawable drawable) {

        gl = drawable.getGL();
        glu = new GLU();

        // setup ogl basics
        gl.glClearDepth(1.0f);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
        gl.glShadeModel(GL.GL_SMOOTH);
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthFunc(GL.GL_LEQUAL);
        gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);

        // setup fbo stuff
        if (!gl.isExtensionAvailable("GL_EXT_framebuffer_object")) {
            System.out.println("Missing: GL_EXT_framebuffer_object");
            System.exit(1);
        }

        final int[] fboHandleBuffer = new int[1];
        gl.glGenFramebuffersEXT(1, fboHandleBuffer, 0);
        fboHandle = fboHandleBuffer[0];

        final int[] rboHandleBuffer = new int[1];
        gl.glGenRenderbuffersEXT(1, rboHandleBuffer, 0);
        rboHandle = rboHandleBuffer[0];

        final int[] glowHandleBuffer = new int[1];
        gl.glGenTextures(1, glowHandleBuffer, 0);
        glowHandle = glowHandleBuffer[0];

        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboHandle);
        gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, rboHandle);
        gl.glBindTexture(GL.GL_TEXTURE_2D, glowHandle);

        gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_LUMINANCE, sunGlowTextureSize, sunGlowTextureSize, 0, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, null);
        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.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT24, sunGlowTextureSize, sunGlowTextureSize);
        gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, GL.GL_RENDERBUFFER_EXT, rboHandle);
        gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_TEXTURE_2D, glowHandle, 0);

        if (gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT) != GL.GL_FRAMEBUFFER_COMPLETE_EXT) {
            System.out.println("Error: GL.GL_FRAMEBUFFER_EXT");
            System.exit(1);
        }

        gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, 0);
        gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);

        // save GL_VIEWPORT for gluProject
        viewport = new int[4];
        gl.glViewport(0, 0, sunGlowTextureSize, sunGlowTextureSize);
        gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
    }

    
    private void setupCamera() {

        final double time = System.currentTimeMillis();

        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        glu.gluLookAt(0.0f, 0.0f, 20.0f, 6 * (float) Math.sin(time / 700), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
    }

    
    private void renderGlowToTexture() {

        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, fboHandle);
        gl.glPushAttrib(GL.GL_VIEWPORT_BIT);
        gl.glViewport(0, 0, sunGlowTextureSize, sunGlowTextureSize);

        setupCamera();

        // save current GL_MODELVIEW_MATRIX for gluProject
        modelview = new double[16];
        gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelview, 0);

        // draw the "sun"
        gl.glTranslatef(sunPosition[0], sunPosition[1], sunPosition[2]);
        glu.gluSphere(glu.gluNewQuadric(), 1, 20, 20);

        gl.glPopAttrib();
        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
    }

    
    private void renderGlowToScreen() {

        // corona parameter
        float spost = 0.0f;
        float alpha = 0.2f;
        
        final int times = 25;
        final float inc = 1.0f / times;
        final float alphainc = alpha / times;

        // calculate current sun position
        final double[] sunPosBuffer = new double[4];
        glu.gluProject(sunPosition[0], sunPosition[1], sunPosition[2], modelview, 0, projection, 0, viewport, 0, sunPosBuffer, 0);

        final float currentSunPosX = (float) (sunPosBuffer[0] / sunGlowTextureSize);
        final float currentSunPosY = (float) (sunPosBuffer[1] / sunGlowTextureSize);

        // draw the glow texture to screen
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        gl.glFrustum(-1.0f, 1.0f, -1, 1, 0.0f, 1.0f);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glLoadIdentity();

        gl.glDisable(GL.GL_DEPTH_TEST);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
        gl.glEnable(GL.GL_BLEND);

        gl.glBindTexture(GL.GL_TEXTURE_2D, glowHandle);
        gl.glBegin(GL.GL_QUADS);

        for (int i = 0; i < times; i++) {

            gl.glColor4f(1.0f, 1.0f, 1.0f, alpha);
            
            gl.glTexCoord2f(spost * currentSunPosX, spost * currentSunPosY);
            gl.glVertex3f(-1.0f, -1.0f, 0.0f);
            
            gl.glTexCoord2f(1 - ((1 - currentSunPosX) * spost), spost * currentSunPosY);
            gl.glVertex3f(1.0f, -1.0f, 0.0f);
            
            gl.glTexCoord2f(1 - ((1 - currentSunPosX) * spost), 1 - ((1 - currentSunPosY) * spost));
            gl.glVertex3f(1.0f, 1.0f, 0.0f);
            
            gl.glTexCoord2f(spost * currentSunPosX, 1 - ((1 - currentSunPosY) * spost));
            gl.glVertex3f(-1.0f, 1.0f, 0.0f);

            spost += inc;
            alpha = alpha - alphainc;
        }

        gl.glEnd();
        gl.glBindTexture(GL.GL_TEXTURE_2D, 0);

        gl.glDisable(GL.GL_BLEND);
        gl.glEnable(GL.GL_DEPTH_TEST);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPopMatrix();
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPopMatrix();
    }

    
    @Override
    public void display(GLAutoDrawable drawable) {

        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        renderGlowToTexture();
        renderGlowToScreen();
    }

    
    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {

        if (height <= 0) height = 1;

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 50.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        // save current GL_PROJECTION_MATRIX for gluProject
        projection = new double[16];
        gl.glGetDoublev(GL.GL_PROJECTION_MATRIX, projection, 0);
    }

    
    @Override
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}

    
    public static void main(String[] args) {

        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);
        canvas.addGLEventListener(new GlowFF());
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                new Thread(new Runnable() {
                    public void run() {
                        animator.stop();
                        System.exit(0);
                    }
                }).start();
            }
        });
        frame.setVisible(true);
        animator.start();
    }

}
```

Optisch schönere glow Effekte lassen sich mit Shadern erzielen, da darüber ein feineres Weichzeichnen möglich ist, im Gegensatz zum oben verwendeten skalieren.

Gruß,
Michael


----------



## jaH.da.izi (1. Sep 2008)

Ich weiss ja überhaupt rein garnicht, wie ich dir danken soll. Irgendwie hatte ich ja schon eine Ahnung, wo die Macke liegt, aber ich kam einfach nicht drauf. Vielen Dank nochmal.


Das mit den Shadern interessiert mich jetzt aber auch irgendwie. Werde mal Google durchforsten und schauen, ob ich was nettes dazu finde. )


Ich schreib dann nochmal, wie ich vorrankomme, bis denn.


----------

