Circular fade in/out filter

I’m woking on a filter to produce a circular fade in/out effect, similar to this.

I have all the code almost finished but the problem is that I don’t know how to cencer the circle on a object’s position.
I’m passing the target position (object global position) to the vert shader. Inside vert shader code, I multiply worldViewProjectionMatrix by targetPosition. Is that ok? How should I center the circle using a geometry position?

CircularFading.vert
[java]
uniform mat4 g_WorldViewProjectionMatrix;
uniform mat4 g_ViewProjectionMatrix;

uniform vec3 m_Target;

attribute vec3 inPosition;
attribute vec2 inTexCoord;

varying vec2 texCoord;
varying vec2 position;

void main() {
vec2 pos = (g_WorldViewProjectionMatrix * vec4(inPosition, 1.0)).xy;
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;

position = (g_ViewProjectionMatrix * vec4(m_Target, 1.0)).xy;

}
[/java]

CircularFading.frag
[java]
uniform float g_Aspect;
uniform vec2 g_Resolution;

uniform sampler2D m_Texture;

varying vec2 texCoord;
varying vec2 position;

void main() {
vec3 color = texture2D(m_Texture, texCoord).rgb;

float radius = 0.3;
vec2 circleCenter = position;

float posX = clamp(circleCenter.x, 0.0, g_Resolution.x) * (g_Aspect);
float posY = clamp(circleCenter.y, 0.0, g_Resolution.y);

float d = distance(vec2(posX, posY), vec2(texCoord.x * (g_Aspect), texCoord.y));

//float vignetting = clamp((0.5 - d) / (0.5 - 0.1), 0.0, 1.0);
float vignetting = d > radius ? 0.0: 1.0;
color.rgb *= vignetting;

gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;

}
[/java]

CircleFadingFilter.java
[java]
public class CircleFadingFilter extends Filter {

/** Geometry position use as Circle center point */
private Vector3f target;

/**
 * Constructor.
 */
public CircleFadingFilter(final Vector3f target) {
    super("OldFilmFilter");
    this.target = target;
}

@Override
protected Material getMaterial() {
    return this.material;
}

@Override
protected void initFilter(final AssetManager manager, final RenderManager renderManager, final ViewPort vp,
        final int w, final int h) {
    this.material = new Material(manager, "ShaderBlow/MatDefs/Filters/FadeCircle/FadeCircle.j3md");
    this.material.setVector3("Target", this.target);
}

}
[/java]

[java]
MaterialDef ColorScale {

MaterialParameters {
    Int NumSamples
    Texture2D Texture
    Vector3 Target
}

Technique {
    VertexShader GLSL100:   ShaderBlow/Filters/FadeCircle/CircleFading.vert
    FragmentShader GLSL100: ShaderBlow/Filters/FadeCircle/FadeCircle.frag

    WorldParameters {
        WorldViewProjectionMatrix
        ViewProjectionMatrix
        Aspect
    }
}

}
[/java]

@H said: I'm passing the target position (object global position) to the vert shader. Inside vert shader code, I multiply worldViewProjectionMatrix by targetPosition. Is that ok?
Nope that's wrong. The name of the matrix is kind of busted to me because it's very unituitive, but the worldViewProjectionMatrix transforms a position in model space to projection space. spaces are like that : model -> world-> view -> projection So if your position is in wrold space you need to multiply it by the viewProjectionMatrix instead to transform it to projection space. Otherwise your approach sounds good.
@nehon said: Nope that's wrong. The name of the matrix is kind of busted to me because it's very unituitive, but the worldViewProjectionMatrix transforms a position in model space to projection space. spaces are like that : model -> world-> view -> projection So if your position is in wrold space you need to multiply it by the viewProjectionMatrix instead to transform it to projection space. Otherwise your approach sounds good.

An aside, the names always made crystal clear sense to me.

worldMatrix = model to world
viewMatrix = world to view
projectionMatrix = view to projection
worldViewProjectionMatrix = model to world to view to projection

Yeah, In my mind the naming makes sense =) but I can see how modelToProjectionMatrix would be more intuitive to understand than worldViewProjectionMatrix =P

@pspeed said: An aside, the names always made crystal clear sense to me.

worldMatrix = model to world
viewMatrix = world to view
projectionMatrix = view to projection
worldViewProjectionMatrix = model to world to view to projection


Well…i found that the fact that the starting space is not mentioned is misleading.
Once you know it of course, you get used to it… but for beginners that’s kind of hard to get it.

Maybe I’ve just been doing OpenGL too long. I didn’t even give it a second thought when I saw it. :-/

I like that worldViewProjectionMatrix has all of the parts in it… so you know that
worldMatrix * viewMatrix * projectionMatrix should be the same thing.
As should worldVieWMatrix * projectionMatrix or worldMatrix * viewProjectionMatrix.

I’ve used this break down so often I can’t even count.

modelToProjection, modelToWorld, etc. would lose that, in my opinion. In the interest of simplicity it would actually be even harder for “noobs” to understand what was happening. You’d now have to have intimate knowledge of what modelToProjection means if you wanted to break it into parts.

I was more thinking of modelWorldViewProjection :stuck_out_tongue:
So all the spaces would have been mentioned.
anyway…we’re hijacking the thread :stuck_out_tongue:

<cite>@nehon said:</cite> So if your position is in wrold space you need to multiply it by the viewProjectionMatrix instead to transform it to projection space. Otherwise your approach sounds good.

Sorry, I put it wrong on the issue description (thanks for the explanation). I’m using the viewProjectiongMatrix. See last line on CircularFading.vert
[java]position = (g_ViewProjectionMatrix * vec4(m_Target, 1.0)).xy;[/java]

I set the geometry’s location to geo.setLocalTranslation(2, 0, 0);. Then I get the geometry’s location as follow geo.getWorldTranslation(); and pass it through CircularFadingFilter constructor.

The shader code that draws the circularFading effect is working fine. The problem is that the circle is not center on the goemetry’s position. I can not find out what is wrong.

What is the Geometry? A quick read of the shader shows that it should be centered around the target… can you confirm that the target location is set properly based on the Geometry?

<cite>@pspeed said:</cite> What is the Geometry? A quick read of the shader shows that it should be centered around the target... can you confirm that the target location is set properly based on the Geometry?

The geometry is the spatial. The test code is:
[java]
public void simpleInitApp() {

    this.assetManager.registerLocator("assets", FileLocator.class);

    this.flyCam.setMoveSpeed(10);

    TestObjectBuilder.buildSkybox(this.assetManager, this.rootNode);
    TestObjectBuilder.buildFloor(this.assetManager, this.rootNode);
    TestObjectBuilder.buildLights(this.rootNode);

    final Spatial char_boy2 = this.assetManager.loadModel("TestModels/LightBlow/jme_lightblow.mesh.xml");
    final Material mat2 = this.assetManager.loadMaterial("TestMaterials/MatCap/MatCapBump1.j3m");
    char_boy2.setMaterial(mat2);
    char_boy2.setLocalTranslation(2, 0, 0);
    TangentBinormalGenerator.generate(char_boy2);
    this.rootNode.attachChild(char_boy2);

    this.fpp = new FilterPostProcessor(this.assetManager);
    final Vector3f target = char_boy2.getWorldTranslation();

    this.circleFadingFilter = new CircularFadingFilter(target);
    this.fpp.addFilter(this.circleFadingFilter);
    this.viewPort.addProcessor(this.fpp);
}

[/java]

I found out that I should convert the spatial position into textCoord in order to draw the circle. So, the vert shader code should be:
[java]
uniform float g_Aspect;
uniform vec2 g_Resolution;

uniform sampler2D m_Texture;

varying vec2 texCoord;
varying vec2 targetPosition;

void main() {
vec3 color = texture2D(m_Texture, texCoord).rgb;

float radius = 0.3;
vec2 circleCenter = targetPosition;

float posX = clamp(circleCenter.x / g_Resolution.x, 0.0, 1.0) * (g_Aspect);
float posY = clamp(circleCenter.y / g_Resolution.y, 0.0, 1.0);

float d = distance(vec2(posX, posY), vec2(texCoord.x * (g_Aspect), texCoord.y));

float vignetting = d &gt; radius ? 0.0: 1.0;
color.rgb *= vignetting;

gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;

}
[/java]

The issue is how to transform a spatial location (i.e: (12, 5, 10)) into a xy coordenates related to the rendered frame.

Projection space coordinates are from -1 to 1. -1,-1 being the bottom left corner of the screen, and 1,1 being the top right corner. (0,0 is the center).
here it looks like you’re assuming that your target position is from 0,0 to resolution.x, resolution.y).

you have coordinates from -1,-1 to 1,1 and you want them from 0 to 1 (like texture coordinates). so you just have to go
[java]

//convert coords from -1,1 to 0,1
vec2 pos = targetPosition *vec2( 0.5) + vec2(0.5) ; // even vec2 pos = targetPosition * 0.5 + 0.5 ; might work here

//then compute the distance
float d = distance(pos, texCoord);

//for vignetting use the step function it’s really faster than branching
color.rgb *= step(radius,d);


[/java]
you don’t need to bother with resolution nor g_Aspect.
here is the spec for the step function http://www.opengl.org/sdk/docs/manglsl/xhtml/step.xml

1 Like

I changed the frag shader code according to what you suggested but it is not working. I think that there is something wrong with targetPosition because the circle’s center is not on the spatial position and if I “hardcoded” the targetPosition to (0,0) then it works great.
[java]
uniform float g_Aspect;

uniform sampler2D m_Texture;

varying vec2 texCoord;
varying vec2 targetPosition;

void main() {
vec3 color = texture2D(m_Texture, texCoord).rgb;

float radius = 0.3;

vec2 circleCenter = targetPosition * vec2(0.5) + vec2(0.5);
//vec2 circleCenter = vec2(0.0) * vec2(0.5) + vec2(0.5);  // USING THIS LINE I GET A CIRCLE WHICH CENTER IS IN THE SCREEN'S CENTER

float d = distance(vec2(circleCenter.x, circleCenter.y), vec2(texCoord.x * (g_Aspect), texCoord.y));

color.rgb *= step(d, radius);

gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;

}
[/java]

<cite>@nehon said:</cite> you don't need to bother with resolution nor g_Aspect.

I use g_Aspect in order to get a circle, otherwise the shape will be a oval.

@H said: I use g_Aspect in order to get a circle, otherwise the shape will be a oval.
oh ok!

oh wait yeah…
I know what’s your issue…
I already got it when doing the water filter. The problem is that your g_ViewProjectionMatrix is not the one you’d expect from the scene camera but from the parallel projection camera that is used to render the full screen quad for the filter. So you can’t get it right with this matrix.

What you can do is to pass the camera.getViewProjectionMatrix() as a mat param (let’s say m_ViewProjectionMatrix).
But if you doing this…you’d better compute directly the target position on the CPU and send it to the frag shader as a material parameter. (you’d have the convenience method in the camera class to do so)

1 Like
@nehon said: ...you'd better compute directly the target position on the CPU and send it to the frag shader as a material parameter. (you'd have the convenience method in the camera class to do so)

Hahah… I was thinking about this post separately and opened it up to suggest the very same thing. :slight_smile:

Provide the target coordinate in screen space instead of making the shader work for it. It was gnawing at the back of my brain the fact that OP is using a varying for something that doesn’t vary.

1 Like

@nehon @pspeed
Thanks very much! That fixed the issue.

I will post a video when I finished the implementation. I’m working on the fading animation (fade in/out).
I have to change the parameters on Filter.preFrame() method according to filter’s javadoc.