We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hello,
Coming from this thread, I am trying to achieve drawing transparent point-based billlboards AND normal opaque geometry masking the billboards.
As you can see by running the sketch, none of the billboards are 'occluded' by solid objects (i.e. you can allways see them, even behind an opaque object). ;(
I'm sure this has to do with depth/depthMask or blending, but I don't know how to fix it.
I've tried method found on the first post of this page, and used gl.glDepthMask(false)
before drawing billboards and gl.glDepthMask(true)
afterwards but could'nt get it to work as expected.
My sketch is simple : draw a solid sphere in the center of the screen, calculate spherical coordinates for billboards, draw the sphere, and draw the billboards (which I'd like to be 'hidden' by the center sphere when they are 'behind' it)..
Code : (reduced as much as possible).
import javax.media.opengl.glu.*;
import javax.media.opengl.GL2;
import com.jogamp.opengl.util.gl2.GLUT;
PShader pointShader;
PVector[] positions;
PVector[] positionsSphere;
color[] colors;
int billboardCount = 200;
Sphere mySphere;
GL2 gl;
void setup()
{
size(1280, 720, OPENGL);
String[] vertSource = new String[] {
"#define PROCESSING_POINT_SHADER",
"uniform mat4 projection;",
"uniform mat4 modelview;",
"uniform float weight;",
"attribute vec4 vertex;",
"attribute vec4 color;",
"attribute vec2 offset;",
"varying vec4 vertColor;",
"varying vec2 texCoord;",
"void main() {",
"vec4 pos = modelview * vertex;",
"vec4 clip = projection * pos;",
"gl_Position = clip + projection * vec4(offset, 0, 0);",
"texCoord = (vec2(0.5) + offset / weight);",
"vertColor = color;",
"}"
};
String[] fragSource = new String[] {
"#ifdef GL_ES",
"precision mediump float;",
"precision mediump int;",
"#endif",
"uniform sampler2D sprite;",
"varying vec4 vertColor;",
"varying vec2 texCoord;",
"void main() {",
"gl_FragColor = texture2D(sprite, texCoord) * vertColor;",
"}"
};
mySphere = new Sphere(width / 2, height / 2, 0);
for(int i = 0; i < billboardCount; i++)
{
mySphere.addSphereItem();
}
pointShader = new PShader(this, vertSource, fragSource);
pointShader.set("sprite", loadImage("https" + "://dl.dropboxusercontent.com/u/31893519/particle.png"));
strokeCap(SQUARE);
}
void drawCenterSphere()
{
pushStyle();
sphereDetail(64);
fill(255);
stroke(0,20);
strokeWeight(0.5f);
sphere(250);
popStyle();
}
void draw()
{
background(200);
lights();
translate(mySphere.xPos, mySphere.yPos ,mySphere.zPos);
rotate(frameCount * 0.01f, 1.0, 0.75, 0.5);
drawCenterSphere();
// render the billboards.
for (SphereItem item : mySphere.items)
{
item.render();
}
}
class Sphere {
float xPos = 0; //X Position of the Sphere
float yPos = 0; //Y Position of the Sphere
float zPos = 0; //Z Position of the Sphere
float radius = 250; //Radius of the Sphere
ArrayList<SphereItem> items = new ArrayList(); //List of all of the items contained in the Sphere
public Sphere(float posX, float posY, float posZ)
{
xPos = posX;
yPos = posY;
zPos = posZ;
}
public void addSphereItem()
{
SphereItem si = new SphereItem(this, random(1.3f ), random(TWO_PI * 2));
items.add(items.size(), si);
};
public void render()
{
for (SphereItem item : items)
{
item.render();
}
}
}
public class SphereItem
{
Sphere parentSphere;
//Spherical Coordinates
float radius;
float theta;
float phi;
//Speed properties
float thetaSpeed = 0;
float phiSpeed = 0;
//Size
float itemSize = 5;
int itemColor;
public SphereItem(Sphere parent, float theta, float phi)
{
parentSphere = parent;
this.theta = theta;
this.phi = phi;
itemSize = random(5f);
thetaSpeed = random(-0.01, 0.01);
phiSpeed = random(-0.01, 0.01);
itemColor = color(random(255),random(255),random(255));
}
public void update()
{
theta += thetaSpeed;
phi += phiSpeed;
}
public void render()
{
float r = parentSphere.radius ;
//Convert spherical coordinates into Cartesian coordinates
float x = cos(theta) * sin(phi) * r;
float y = sin(theta) * sin(phi) * r;
float z = cos(phi) * r;
update();
renderAsBillboards (x, y, z);
}
public void renderAsBillboards(float x, float y, float z)
{
float size = itemSize *20;
shader(pointShader, POINTS);
pointShader.set("weight", size);
blendMode(BLEND);
hint(DISABLE_DEPTH_TEST);
stroke(itemColor);
strokeWeight(size);
point(x,y,z);
hint(ENABLE_DEPTH_TEST);
resetShader();
blendMode(BLEND);
}
}
The billboard rendering code is located in the SphereItem class at line 173.
As you can notice, we can see the billboards through the center sphere, as if the sphere was transparent (and it is not !)..
So my question is :
How to modify this sketch in order to 'normally' occlude billboards so that they don't appear when drawn behind an opaque object ?
@poersch, @codeanticode, an idea ?
I'm really stuck on this one.
Answers
Use DISABLE_DEPTH_MASK/ENABLE_DEPTH_MASK instead of DISABLE_DEPTH_TEST/ENABLE_DEPTH_TEST, and the sphere will occlude the billboards, but they won't interfere with each other (because their depth values are not being written to the depth buffer, but the depth test is still being performed).
@codeanticode, thanks again for your patience ;-)
But, which
blendMode()
value should I define (at line 178) ?I have tried all values (from
blendMode()
documentation, and the only one where I don't see the quads is :blendMode(ADD)
..Very nice, but this is additive blending..
How can I at the same time :
?
(Note: I have 'rejected' your answer though it was part of the solution, because the forum keeps telling me about accept / reject, and I wanted to ask this additional question. No offense here ;) )
Just add a little offset to the SphereItem position in order to avoid (visually) SphereItem/CenterSphere-intersections (line 127 in the following code). Additionally I optimized your code (a little) and commented most of the changes:
Btw.: You'll notice that the particles aren't depth sorted correctly, that's because Processing doesn't depth sort geometry, you'll have to do it on your own.