Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment with glsl based turing patterns #11

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions processing-app/src/main/java/glslfft/AppletTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package glslfft;

import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import processing.core.PApplet;

import java.lang.reflect.Method;

public class AppletTest extends PApplet {

@Override
public void settings() {
size(0, 0, P2D);
}

@Override
public void setup() {

StringBuilder result = new StringBuilder();
try {
for(Method m : this.getClass().getDeclaredMethods()) {
if(m.getName().startsWith("test") && m.getParameterCount() == 0) {
boolean success;
try {
println("==> Starting: " + m.getName());
m.invoke(this);
success = true;
} catch(Exception e) {
e.printStackTrace(System.out);
success = false;
}
println("<== Finished: " + m.getName());
if(success) {
result.append(m.getName()).append(": ✅ \n");
} else {
result.append(m.getName()).append(": ❌ \n");
}
}
}
} finally {
println("\nTest Results Summary: ");
println(result);
exit();
}
}
}
64 changes: 64 additions & 0 deletions processing-app/src/main/java/glslfft/FftBlurTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package glslfft;

import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLSLProgram;
import com.thomasdiewald.pixelflow.java.imageprocessing.filter.DwFilter;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.opengl.PGraphicsOpenGL;

import java.util.List;

public class FftBlurTest extends PApplet {
private DwPixelFlow context;
private GlslFft fft;

@Override
public void settings() {
size(512, 256, P2D);
}

@Override
public void setup() {
context = new DwPixelFlow(this);
fft = new GlslFft(context);

DwGLSLProgram shader = context.createShader("glslfft/circle-kernel.frag");
context.begin();
context.beginDraw((PGraphicsOpenGL) g);

shader.begin();
shader.uniform2f("resolution", (float) width, (float) height);
shader.drawFullScreenQuad();
shader.end();

context.endDraw();
context.end();

// PGraphics d = createGraphics(width, height, P2D);
//
// d.beginDraw();
// d.background(0);
// d.fill(255);
// d.ellipse(width/2f, height/2f, 200, 200);
// d.endDraw();

// GlslFft.FftBuffer in = fft.newBuffer(width, height);
// GlslFft.FftBuffer ping = fft.newBuffer(width, height);
// GlslFft.FftBuffer pong = fft.newBuffer(width, height);
// GlslFft.FftBuffer out = fft.newBuffer(width, height);
//
// List<FftPass<GlslFft.FftBuffer>> passes = fft.forward(in, ping, pong, out, width, height);
// fft.runPasses(passes);

// TODO: kernel, multiply, inverse

// DwFilter.get(context).copy.apply(d, in.buf);
// DwFilter.get(context).copy.apply(out.buf, (PGraphicsOpenGL) g);

}

public static void main(String[] args) {
PApplet.main(FftBlurTest.class);
}
}
2 changes: 2 additions & 0 deletions processing-app/src/main/java/glslfft/FftPass.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ public class FftPass<B> {
// The output buffer is being filled by this pass
B output;

// Normalisation factor used for the inverse transform
float normalization;

float subtransformSize;

// 1/width
Expand Down
75 changes: 33 additions & 42 deletions processing-app/src/main/java/glslfft/GlslFft.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,26 @@
import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLSLProgram;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLTexture;
import processing.core.PApplet;
import turingpatterns.Complex;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import static processing.core.PApplet.print;
import static processing.core.PApplet.println;
import static processing.core.PApplet.*;

/**
* This class provides a simplified interface to run an FFT on the GPU
* using OpenGL and a GLSL fragment shader.
*
* <p>
* This is a port of the glsl-fft javascript library that can be found here https://github.com/rreusser/glsl-fft
*/
public class GlslFft {

private final DwPixelFlow context;
private final DwGLSLProgram fft;

public GlslFft(PApplet applet) {
this.context = new DwPixelFlow(applet);
public GlslFft(DwPixelFlow context) {
this.context = context;
this.fft = this.context.createShader("glslfft/fft.frag");
}

Expand Down Expand Up @@ -107,22 +104,16 @@ private <B> List<FftPass<B>> fftPasses(
} else {
pass.output = pong;
}

if (i == 0) {
if (pass.forward) {
pass.normalization = 1;
} else {
pass.normalization = 1.0f / width / height;
}
} else {
pass.normalization = 1;
pass.normalization = 1.0f;
if (i == 0 && !pass.forward) {
pass.normalization = 1.0f / width / height;
}

pass.subtransformSize = (float) Math.pow(2, (pass.horizontal ? i : (i - xIterations)) + 1);

passes.add(pass);

// Swap the image buffers
// Swap the buffers
B tmp = ping;
ping = pong;
pong = tmp;
Expand All @@ -136,7 +127,7 @@ private <B> List<FftPass<B>> fftPasses(
*/
public void runPasses(List<FftPass<FftBuffer>> passes) {
int count = 0;
boolean debug = false;
boolean debug = true;
for (FftPass<FftBuffer> pass : passes) {
this.context.begin();
this.context.beginDraw(pass.output.buf);
Expand All @@ -158,10 +149,10 @@ public void runPasses(List<FftPass<FftBuffer>> passes) {

if(debug) {
println("------------- Pass " + count + ": Inputs -------------");
printComplex(pass.input);
println(format(loadLayer0(pass.input)));

println("------------- Pass " + count + ": Outputs -------------");
printComplex(pass.output);
println(format(loadLayer0(pass.output)));
println();
}
count++;
Expand All @@ -173,9 +164,9 @@ public void runPasses(List<FftPass<FftBuffer>> passes) {
* We can pack two complex number matrices into the four channels of one texture.
*/
public FloatBuffer prepare(Complex[][] data0, Complex[][] data1, int width, int height) {
if (width != height) {
throw new IllegalArgumentException("For some reason the glsl based FFT doesn't work on non-square data and I haven't debugged it yet");
}
// if (width != height) {
// throw new IllegalArgumentException("For some reason the glsl based FFT doesn't work on non-square data and I haven't debugged it yet");
// }
float[] fdata = new float[width * height * 4];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Expand Down Expand Up @@ -216,7 +207,7 @@ private Complex[][] loadResult(GlslFft.FftBuffer fft, int offset) {
float[] data = fft.buf.getFloatTextureData(new float[width * height * 4]);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int idx = 4 * (y * fft.buf.w + x);
int idx = 4 * (y * width + x);
float re = data[idx + offset];
float im = data[idx + offset + 1];
result[y][x] = new Complex(re, im);
Expand All @@ -225,6 +216,19 @@ private Complex[][] loadResult(GlslFft.FftBuffer fft, int offset) {
return result;
}

public String format(Complex[][] data) {
StringBuilder buffer = new StringBuilder();
int ydim = data.length;
int xdim = data[0].length;
for (int y = 0; y < ydim; y++) {
for (int x = 0; x < xdim; x++) {
buffer.append(data[y][x]).append(", ");
}
buffer.append("\n");
}
return buffer.toString();
}

/**
* Create a new buffer the the provided dimensions that is configured to hold 2 complex number matrices.
*/
Expand All @@ -240,26 +244,12 @@ public FftBuffer newBuffer(int width, int height) {
* {@link #prepare(Complex[][], Complex[][], int, int)}
*/
public FftBuffer newBuffer(int width, int height, FloatBuffer data) {
if (width != height) {
throw new IllegalArgumentException("For some reason the glsl based FFT doesn't work on non-square data and I haven't debugged it yet");
}
// if (width != height) {
// throw new IllegalArgumentException("For some reason the glsl based FFT doesn't work on non-square data and I haven't debugged it yet");
// }
return new FftBuffer(this.context, width, height, data);
}

private void printComplex(GlslFft.FftBuffer fft) {
float[] data = fft.buf.getFloatTextureData(new float[4 * 4 * 4]);
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
int idx = 4 * (y * fft.buf.w + x);
float real = data[idx];
float img = data[idx + 1];

print(real + "+" + img + "j, ");
}
println();
}
}

public static class FftBuffer {
// RGBA texture, with 32-bit floats in each channel
DwGLTexture buf = new DwGLTexture();
Expand All @@ -268,7 +258,8 @@ private FftBuffer(DwPixelFlow context, int width, int height, FloatBuffer data)
this.buf.resize(
context,
GL2.GL_RGBA32F,
width, height,
width,
height,
GL2.GL_RGBA,
GL2.GL_FLOAT,
GL2.GL_NEAREST,
Expand Down
Loading