Fast Volumetric AntiAliased Lines
in
Share your Work
•
2 years ago
I've been looking for a way to create fast lines for a while now, since I draw a lot of them in my sketch and it really slows performance when you have 10,000 plus lines. Seems
simple lines
are generally not hardware accelerated. I think I've found a solution though and wanted to share. It's a small port of
Sebastien Hillaire's work. I hope to be able to convert it into a VBO, and I'll repost if I'm successful.
It renders a volumetric line by using billboard constrain on an axis. On each line's extremity, I had a camera facing billboard again constrain by the line axis. 6 triangles are used for each lines (in TRIS_STRIP mode) and a fast GLSL shader : all tricks are performed in the vertex shader, the fragment shader contains only a texture access for the line appearance.
Here is the code that I've been testing... and a link to the texture.
It renders a volumetric line by using billboard constrain on an axis. On each line's extremity, I had a camera facing billboard again constrain by the line axis. 6 triangles are used for each lines (in TRIS_STRIP mode) and a fast GLSL shader : all tricks are performed in the vertex shader, the fragment shader contains only a texture access for the line appearance.
Here is the code that I've been testing... and a link to the texture.
- import peasy.PeasyCam; //peasy camera control
- import processing.opengl.PGraphicsOpenGL;
- import com.sun.opengl.util.texture.Texture;
- import com.sun.opengl.util.texture.TextureIO;
- import javax.media.opengl.GL;
- import java.nio.ByteBuffer;
- import com.sun.opengl.util.BufferUtil;
- PeasyCam cam;
- PGraphicsOpenGL pgl;
- GL gl;
- Texture linetex;
- int nbVLine = 12;
- VolumeLineRenderer vr;
- void setup() {
- size(1000,800,OPENGL);
- pgl = (PGraphicsOpenGL) g;
- gl = pgl.gl;
- ambientLight(255, 255, 255, 0,0,0);
- cam = new PeasyCam(this, 200);
- try {
- linetex = TextureIO.newTexture(new File(dataPath("glow.bmp")), true); // http://farm6.static.flickr.com/5009/5307555840_7ff73e086e.jpg
- }
- catch (IOException e) {
- println(e);
- }
- vr = new VolumeLineRenderer();
- vr.lineWidth(1);
- }
- void draw() {
- background(0);
- pgl.beginGL();
- linetex.enable();
- linetex.bind();
- gl.glEnable(GL.GL_CULL_FACE);
- gl.glFrontFace(GL.GL_CCW);
- gl.glCullFace(GL.GL_BACK);
- gl.glDepthMask(false);
- gl.glDepthFunc(GL.GL_ALWAYS);
- gl.glEnable(GL.GL_BLEND);
- gl.glBlendFunc(GL.GL_ONE,GL.GL_ONE);
- vr.beginRender();
- for(int vl=0;vl<nbVLine;vl++)
- {
- gl.glColor3fv(vlineColor[vl], 0);
- vr.renderLine(vline[vl*2],vline[vl*2+1]);
- }
- vr.endRender();
- gl.glDisable(GL.GL_BLEND);
- gl.glDepthFunc(GL.GL_LESS);
- gl.glDepthMask(true);
- gl.glDisable(GL.GL_CULL_FACE);
- // pshader.endShader();
- linetex.disable();
- pgl.endGL();
- }
- class VolumeLineRenderer {
- int textureID, gpuProgVolumeLine_lineTex;
- //int gpuProgVolumeLine_other;
- int gpuProgVolumeLine_other;
- float lWidth;
- int gpuProgVolumeLine_lineWidth;
- boolean isRendering;
- VolumeLineRenderer volumeLineRenderer = null;
- GLSLShader gpuProgVolumeLine = null;
- PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
- public VolumeLineRenderer() {
- this.textureID = 0;
- this.lWidth = 1.0;
- this.isRendering = false;
- gl = pgl.gl;
- this.gpuProgVolumeLine = new GLSLShader(gl);
- File lvs = new File(dataPath("vshader.vert"));
- File lfs = new File(dataPath("fshader.frag"));
- if (lvs.exists() && lfs.exists()) {
- try {
- gpuProgVolumeLine.loadVertexShader(lvs.toString());
- gpuProgVolumeLine.loadFragmentShader(lfs.toString());
- gpuProgVolumeLine.useShaders();
- }
- catch (Exception e) {
- exit();
- }
- }
- else {
- exit();
- }
- this.gpuProgVolumeLine_other = this.gpuProgVolumeLine.getAttribLocation("other");
- this.gpuProgVolumeLine_lineWidth = this.gpuProgVolumeLine.getUniformLocation("lineWidth");
- this.gpuProgVolumeLine_lineTex = this.gpuProgVolumeLine.getUniformLocation("lineTex");
- }
- public VolumeLineRenderer getInstance()
- {
- if(volumeLineRenderer==null) {
- volumeLineRenderer = new VolumeLineRenderer();
- }
- return volumeLineRenderer;
- }
- public void destroyInstance() {
- if(volumeLineRenderer!=null) {
- volumeLineRenderer = null;
- }
- }
- public void lineTexture(int texID)
- {
- this.textureID = texID;
- }
- public void lineWidth(float lwidth) {
- if (lwidth > 0.0f) {
- this.lWidth = lwidth;
- }
- }
- public void beginRender() {
- if(this.isRendering) {
- return;
- }
- this.isRendering = true;
- this.gpuProgVolumeLine.startShader();
- gl.glUniform1iARB(this.gpuProgVolumeLine_lineTex, 0);
- gl.glUniform1fARB(this.gpuProgVolumeLine_lineWidth, this.lWidth);
- }
- public void renderLine (float[] p1, float[] p2) {
- gl.glBegin(gl.GL_TRIANGLE_STRIP);
- gl.glNormal3fv(vline_vertexOffset[0], 0);
- gl.glTexCoord2f(vline_texCoord[0][0],vline_texCoord[0][1]);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p2[0], p2[1], p2[2], 1.0f);
- gl.glVertex3f(p1[0],p1[1],p1[2]);
- gl.glNormal3fv(vline_vertexOffset[1], 0);
- gl.glTexCoord2f(vline_texCoord[1][0], vline_texCoord[1][1]);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p2[0], p2[1], p2[2], 1.0f);
- gl.glVertex3fv(p1, 0);
- gl.glNormal3fv(vline_vertexOffset[2], 0);
- gl.glTexCoord2fv(vline_texCoord[2], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p2[0], p2[1], p2[2], 1.0f);
- gl.glVertex3fv(p1, 0);
- gl.glNormal3fv(vline_vertexOffset[3], 0);
- gl.glTexCoord2fv(vline_texCoord[3], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p2[0], p2[1], p2[2], 1.0f);
- gl.glVertex3fv(p1, 0);
- gl.glNormal3fv(vline_vertexOffset[4], 0);
- gl.glTexCoord2fv(vline_texCoord[4], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p1[0], p1[1], p1[2], 1.0f);
- gl.glVertex3fv(p2, 0);
- gl.glNormal3fv(vline_vertexOffset[5], 0);
- gl.glTexCoord2fv(vline_texCoord[5], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p1[0], p1[1], p1[2], 1.0f);
- gl.glVertex3fv(p2, 0);
- gl.glNormal3fv(vline_vertexOffset[6], 0);
- gl.glTexCoord2fv(vline_texCoord[6], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p1[0], p1[1], p1[2], 1.0f);
- gl.glVertex3fv(p2, 0);
- gl.glNormal3fv(vline_vertexOffset[7], 0);
- gl.glTexCoord2fv(vline_texCoord[7], 0);
- gl.glVertexAttrib4f(this.gpuProgVolumeLine_other, p1[0], p1[1], p1[2], 1.0f);
- gl.glVertex3fv(p2, 0);
- gl.glEnd();
- }
- public void endRender()
- {
- if(!this.isRendering) {
- return;
- }
- this.isRendering = false;
- this.gpuProgVolumeLine.endShader();
- }
- }
- class GLSLShader
- {
- GL gl;
- int programObject;
- int vertexShader;
- int fragmentShader;
- GLSLShader(GL gl0)
- {
- gl = gl0;
- programObject = gl.glCreateProgramObjectARB();
- vertexShader = -1;
- fragmentShader = -1;
- }
- void deleteShader() {
- gl.glUseProgramObjectARB(0);
- try {
- gl.glDetachObjectARB(programObject, vertexShader);
- gl.glDeleteObjectARB(vertexShader);
- gl.glDetachObjectARB(programObject, fragmentShader);
- gl.glDeleteObjectARB(fragmentShader);
- gl.glDeleteObjectARB(programObject);
- }
- catch (Exception e) {
- }
- }
- void loadVertexShader(String file)
- {
- String shaderSource = join(loadStrings(file), "\n");
- vertexShader = gl.glCreateShaderObjectARB(GL.GL_VERTEX_SHADER_ARB);
- gl.glShaderSourceARB(vertexShader, 1, new String[] {
- shaderSource
- }
- , (int[]) null, 0);
- gl.glCompileShaderARB(vertexShader);
- checkLogInfo(gl, vertexShader);
- gl.glAttachObjectARB(programObject, vertexShader);
- }
- void loadFragmentShader(String file)
- {
- String shaderSource = join(loadStrings(file), "\n");
- fragmentShader = gl.glCreateShaderObjectARB(GL.GL_FRAGMENT_SHADER_ARB);
- gl.glShaderSourceARB(fragmentShader, 1, new String[] {
- shaderSource
- }
- ,(int[]) null, 0);
- gl.glCompileShaderARB(fragmentShader);
- checkLogInfo(gl, fragmentShader);
- gl.glAttachObjectARB(programObject, fragmentShader);
- }
- int getAttribLocation(String name)
- {
- return(gl.glGetAttribLocationARB(programObject, name));
- }
- int getUniformLocation(String name)
- {
- return(gl.glGetUniformLocationARB(programObject, name));
- }
- void useShaders()
- {
- gl.glLinkProgramARB(programObject);
- gl.glValidateProgramARB(programObject);
- checkLogInfo(gl, programObject);
- }
- void startShader()
- {
- gl.glUseProgramObjectARB(programObject);
- }
- void endShader()
- {
- gl.glUseProgramObjectARB(0);
- }
- void checkLogInfo(GL gl, int obj)
- {
- java.nio.IntBuffer iVal = BufferUtil.newIntBuffer(1);
- gl.glGetObjectParameterivARB(obj, GL.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal);
- int ilength = iVal.get();
- if (ilength <= 1) return;
- ByteBuffer infoLog = BufferUtil.newByteBuffer(ilength);
- iVal.flip();
- gl.glGetInfoLogARB(obj, ilength, iVal, infoLog);
- byte[] infoBytes = new byte[ilength];
- infoLog.get(infoBytes);
- }
- }
- float[][] vline_texCoord = { //[8][3]
- {
- 1.0f, 0.0f, 0.0f
- }
- ,
- {
- 1.0f, 1.0f, 0.0f
- }
- ,
- {
- 0.5f, 0.0f, 0.0f
- }
- ,
- {
- 0.5f, 1.0f, 0.0f
- }
- ,
- {
- 0.5f, 0.0f, 0.0f
- }
- ,
- {
- 0.5f, 1.0f, 0.0f
- }
- ,
- {
- 0.0f, 0.0f, 0.0f
- }
- ,
- {
- 0.0f, 1.0f, 0.0f
- }
- };
- /**
- * Les offsets des vertices par rapport ‡ la ligne normale
- */
- float[][] vline_vertexOffset= { //[8][2]
- {
- 1.0f, 1.0f
- }
- , /////
- {
- 1.0f, -1.0f
- }
- , // par rapport
- {
- 0.0f, 1.0f
- }
- , // au vertex A
- {
- 0.0f, -1.0f
- }
- , /////
- {
- 0.0f, -1.0f
- }
- , /////
- {
- 0.0f, 1.0f
- }
- , // par rapport
- {
- 1.0f, -1.0f
- }
- , // au vertex B
- {
- 1.0f, 1.0f
- } /////
- };
- float vline[][] =
- {
- {
- 0.0f, 0.0f, 0.0f
- }
- , {
- 100.0f, 0.0f, 0.0f
- }
- ,
- {
- 0.0f, 0.0f, 0.0f
- }
- , {
- 0.0f, 100.0f, 0.0f
- }
- ,
- {
- 0.0f, 0.0f, 0.0f
- }
- , {
- 0.0f, 0.0f, 100.0f
- }
- ,
- {
- 150.0f, 0.0f, 0.0f
- }
- , {
- 250.0f, 0.0f, 0.0f
- }
- ,
- {
- 150.0f, 0.0f, 0.0f
- }
- , {
- 150.0f, 10.0f, 0.0f
- }
- ,
- {
- 150.0f, 0.0f, 0.0f
- }
- , {
- 150.0f, 0.0f, 100.0f
- }
- ,
- {
- 100.0f, 0.0f, 200.0f
- }
- , {
- 200.0f, 50.0f, 250.0f
- }
- ,
- {
- 200.0f, 50.0f, 250.0f
- }
- , {
- 300.0f, -10.0f, 220.0f
- }
- ,
- {
- 300.0f, -10.0f, 220.0f
- }
- , {
- 350.0f, 20.0f, 200.0f
- }
- ,
- {
- -60.0f, 100.0f, 30.0f
- }
- , {
- -50.0f, 0.0f, 20.0f
- }
- ,
- {
- -60.0f, 0.0f, 20.0f
- }
- , {
- -30.0f, 150.0f, -20.0f
- }
- ,
- {
- -20.0f, 110.0f, -10.0f
- }
- , {
- -60.0f, 30.0f, 0.5f
- }
- };
- float vlineColor[][] =
- {
- {
- 1.0f, 0.0f, 0.0f
- }
- ,
- {
- 0.0f, 1.0f, 0.0f
- }
- ,
- {
- 0.0f, 0.0f, 1.0f
- }
- ,
- {
- 1.0f, 1.0f, 1.0f
- }
- ,
- {
- 1.0f, 1.0f, 1.0f
- }
- ,
- {
- 1.0f, 1.0f, 1.0f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- ,
- {
- 1.0f, 1.0f, 0.2f
- }
- };
- attribute vec4 other;
- uniform float lineWidth;
- varying vec4 color;
- void main() {
- gl_TexCoord[0] = gl_MultiTexCoord0;
- color = gl_Color;
- vec4 vMVP = gl_ModelViewProjectionMatrix * gl_Vertex;
- vec4 otherMVP = gl_ModelViewProjectionMatrix * other;
- vec2 lineDirProj = lineWidth * normalize( (vMVP.xy/vMVP.w) - (otherMVP.xy/otherMVP.w) );
- if( sign(otherMVP.w) != sign(vMVP.w) ) {
- lineDirProj = -lineDirProj;
- }
- vMVP.x = vMVP.x + lineDirProj.x * gl_Normal.x;
- vMVP.y = vMVP.y + lineDirProj.y * gl_Normal.x;
- vMVP.x = vMVP.x + lineDirProj.y * gl_Normal.y;
- vMVP.y = vMVP.y - lineDirProj.x * gl_Normal.y;
- gl_Position = vMVP;
- }
- uniform sampler2D lineTex;
- varying vec4 color;
- void main() {
- gl_FragColor = texture2D(lineTex, gl_TexCoord[0].st) * color;
- }