stackgl / headless-gl

🎃 Windowless WebGL for node.js
1.76k stars 169 forks source link

Anti-aliasing support #30

Open bsergean opened 9 years ago

bsergean commented 9 years ago

It looks like Browser anti-alias by default, while the antialiasing parameters given at context creation does not impact the rendering.

Adding a screen-shot that compares a Chrome render with a slightly jagged headless-gl render.

screen shot 2015-09-14 at 8 48 25 am

It's unclear to me what should be done, there might be fixes needed in multiple areas, maybe the only fix needs to be on the user side by calling glEnagle(gl.MULTI_SAMPLE) or something, and I don't know if the GL context needs something special as well.

There are workaround too, such as some GLSL post-processing, but if we can get this for free that would be much easier. Some googling led me to this -> https://github.com/mattdesl/glsl-fxaa

mikolalysenko commented 9 years ago

Unfortunately, gl.MULTISAMPLE is not part of OpenGL ES2. I think it is possible to hack something like this on top of ANGLE, but it would require some care.

bsergean commented 9 years ago

OK. FYI The problem is more noticeable when I'm trying to render fonts. I'm investigating BMFont now, I'll see how it goes.

Found something on stack overflow about this: http://stackoverflow.com/questions/7379710/how-to-do-multisampling-in-android-opengl-es / where they say that EGL can be given a sample buffer count. Haven't look deeper into that.

If you want to do FSAA, you need to create an EGL context with multisampling enabled. Write an EGLConfigChooser that returns a multisampling config (specify 1 for EGL_SAMPLE_BUFFERS), and pass it to setEGLConfigChooser.
mikolalysenko commented 9 years ago

Unfortunately that trick won't work for headless-gl, since it creates an FBO for the drawing buffer. So you can't set the MSAA flag on the default draw surface for the context :(.

bsergean commented 9 years ago

Was looking at this (see below). Is it that this is not possible for FBO, only for direct drawing on the screen ?

Found this here, to create a window https://github.com/google/angle/blob/master/util/EGLWindow.cpp

EGL_SAMPLE_BUFFERS, mMultisample ? 1 : 0 / EGL_SAMPLE_BUFFERS, mMultisample ? 1 : 0,

In the headless-gl code:

  //Set up configuration
  std::vector<EGLint> attrib_list;

  #define PUSH_ATTRIB(x, v) \
    attrib_list.push_back(x);\
    attrib_list.push_back(v);

  PUSH_ATTRIB(EGL_SURFACE_TYPE, EGL_PBUFFER_BIT);
  //PUSH_ATTRIB(EGL_CONFORMANT, EGL_OPENGL_ES2_BIT);
  PUSH_ATTRIB(EGL_RED_SIZE, 8);
  PUSH_ATTRIB(EGL_GREEN_SIZE, 8);
  PUSH_ATTRIB(EGL_BLUE_SIZE, 8);
  if(alpha) {
    PUSH_ATTRIB(EGL_ALPHA_SIZE, 8);
  } else {
    PUSH_ATTRIB(EGL_ALPHA_SIZE, 0);
  }
  if(depth) {
    PUSH_ATTRIB(EGL_DEPTH_SIZE, 24);
  } else {
    PUSH_ATTRIB(EGL_DEPTH_SIZE, 0);
  }
  if(stencil) {
    PUSH_ATTRIB(EGL_STENCIL_SIZE, 8);
  } else {
    PUSH_ATTRIB(EGL_STENCIL_SIZE, 0);
  }

  attrib_list.push_back(EGL_NONE);

  #undef PUSH_ATTRIB

  EGLint num_config;
  if(!eglChooseConfig(
      DISPLAY,
      &attrib_list[0],
      &config,
      1,
      &num_config) ||
      num_config != 1) {
    state = GLCONTEXT_STATE_ERROR;
    return;
  }
mikolalysenko commented 9 years ago

The default surface doesn't get used for the fbo. The reason for this is that reallocating the surface crashes ANGLE on os x for some reason. So instead we use a dummy fbo, which is handled in the webgl.js wrapper.

bsergean commented 9 years ago

Ok ... Looks like there might be extension per OS (glRenderBufferStorageMultisampleAPPLE for Mac); not sure about Linux / Mesa where I would need this...

https://answers.madewithmarmalade.com/questions/13839/is-it-possible-to-use-multisampling-on-offscreen-s.html

bsergean commented 9 years ago

Ah ok. It is a bit encouraging then, if we get to the bottom of the crash on ANGLE on osx that could be fixed. I wonder how it would behave on Linux, actually I'm not even sure I looked at whether multi-sampling was broken on Linux or not.

bsergean commented 9 years ago

Hi there, I found this to request multi-samples for FBO: ANGLE_framebuffer_multisample

https://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_framebuffer_multisample.txt

glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEANGLEPROC)eglGetProcAddress("glRenderbufferStorageMultisampleANGLE");

Also, when "browsing the web", it looks like most browser implement WebGL by rendering offscreen into FBO, so there has to be way to support this / I wonder if we could just call into one function exposed by ANGLE to get this.

This link is the firefox bug to implement anti-aliasing in their WebGL implementation. It contains links to patches that could be good to look at.

https://bugzilla.mozilla.org/show_bug.cgi?id=615976

There is this too: EXT_framebuffer_multisample

... lots of links, not too many patches :) / I'll see if I can dig into the code to propose more than just ideas :)

mikolalysenko commented 9 years ago

Thanks! I'll take a look at this as soon as I get a chance. I'm currently at a conference, but I should be back in Madison by Friday.

bsergean commented 9 years ago

Is it the js code that creates the so called "dummy" fbo ?

function allocateDrawingBuffer(context, width, height) {

  context._drawingBuffer = new WebGLDrawingBufferWrapper(
    _createFramebuffer.call(context),
    _createTexture.call(context),
    _createRenderbuffer.call(context))

  resizeDrawingBuffer(context, width, height)
}

I guess I'm trying to help but I don't really know where to start ... do you expect that the fix could be solely inside of libANGLE, say for Linux by passing the good arguments for context creation in libANGLE/renderer/gl/glx/DisplayGLX.

Or should it requires modifications to the js code as well ? And I realize that fixing it yourself might be less work than answering my questions, but hey, here they are anyway !

mikolalysenko commented 9 years ago

That is the JS code where it generates the offscreen FBO. The other function you should look at is resizeDrawingBuffer() which sets all the parameters.

bsergean commented 9 years ago

As an update, I was able to use https://github.com/mattdesl/glsl-fxaa (by shamelessly extracting the shaders so I wouldn't have to setup glslify, I think I also had minor three.js incompatibilities or maybe coding problem).

My current approach which might be suboptimal is to render my scene as usual, read back the pixels and achieves anti-aliasing using a post-processing pass (setup an ortho camera, display the aliased pixel buffer as a textured quad, and execute the glsl magic from glsl-fxaa). This works very nicely, I noticed some small artifacts, but by rendering at a high resolution (1600x1200) and then scaling down the image (I'm inserting them into a PDF) the image looks really good.

whatisor commented 8 years ago

Hello, What is the final solution of Anti-aliasing support? Thank you

bsergean commented 8 years ago

For reference, here is a gist that explains how I got fxaa anti-aliasing to work without native headless-gl support.

https://gist.github.com/bsergean/0d79ce3c7384cf6d1bb6

bsergean commented 8 years ago

Here's a post by Ben Houston on a somewhat related three.js issue, for reference. This solution is using an extension, and I can remember if it is OK to use given that the low level GL code is done with ANGLE (and if the extension exists on OSX).

If you want to setup a MSAA buffer, I can share this code for anyone to use that does that -- it is used in our Exocortex Fury renderer product and its been tested on both Linux and Windows on a ton of GPUs over the past 6 years:

EStatus GPUParticleRenderer::createOffscreenMultisampleBuffer() {

// http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample

int maxSamples;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);

int samples = _frameSpec.getNumMultiSamples();
if ( samples > maxSamples )
{
    char szBuffer[1024];
    sprintf_s( szBuffer, 1024, "The requested number of multisamples (%i) is greater than the maximum supported (%i)", samples, maxSamples );
    return EStatus::FailureCode( szBuffer );
}

glGenFramebuffersEXT(1, &multiSampleTarget.fboId);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, multiSampleTarget.fboId);

glGenRenderbuffersEXT(1, &multiSampleTarget.colorBufferId);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, multiSampleTarget.colorBufferId);

GL( glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, GL_RGBA32F_ARB, _frameSpec.getImageSize().x, _frameSpec.getImageSize().y) );

glGenRenderbuffersEXT(1, &multiSampleTarget.depthBufferId);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, multiSampleTarget.depthBufferId);

GL( glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, GL_DEPTH_COMPONENT24, _frameSpec.getImageSize().x, _frameSpec.getImageSize().y) );

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, multiSampleTarget.colorBufferId);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, multiSampleTarget.depthBufferId);

multiSampleTarget._init = true;     // can now be deleted!

char szErrorMessage[1024];
if( glExtCheckFramebufferStatus(szErrorMessage) != 1 )
{
    char szBuffer[2048];
    sprintf_s( szBuffer, 1024, "Unable to allocate framebuffers because: %s", szErrorMessage );
    return EStatus::FailureCode( szBuffer );
}
return EStatus::SuccessCode();

}

Then just before we render, we do this:

if (_frameSpec.getNumMultiSamples() > 0) {
    GL( glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, multiSampleTarget.fboId ) );
}
else {
    GL( glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, singleSampleTarget.fboId ) );
}
whatisor commented 8 years ago

Is it possible for you to support MSAA on headless-gl? :D Thank you so much

bhouston commented 8 years ago

I've done a bit of research and multi-sampling is built into ANGLE. The main place to enable it appears to be in the WebGL.cc where one specifies the attrib_list[] that is passed into eglChooseConfig().

I believe one has to add something like:

 //Set up configuration
  EGLint attrib_list[] = {
 ...
   , EGL_SAMPLES, ( antialiasing ? 2 : 0 ),
    , EGL_NONE
  };

This would request a config with at least 2 samples per pixel. I could be wrong. I need to investigate it more.

joshunger commented 8 years ago

@bhouston, take a look at this post from a year ago https://groups.google.com/forum/#!topic/angleproject/mlkTRfwjcxQ

We don't currently support mutlisampling the EGL surfaces, the EGL configs returned will all have EGL_SAMPLE_BUFFERS set to 0.

I'm wondering if ANGLE implemented it recently? Let me know what you find.

I was also looking at the source code for the Apple CGL implementation and it appears samples are hardcoded to 0. See https://github.com/google/angle/blob/master/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm#L195

And the GLX implementation https://github.com/google/angle/blob/master/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp

I'd be interested in helping. Let me know if you can figure out how to get a config returned with samples. I'm guessing you'll get zero configs returned.

bhouston commented 8 years ago

@joshunger, I find ANGLE a bit hard to understand, I'm still somewhat overwhelmed by it. I guess I am missing a high level overview of ANGLE. Do you know where one is?

The GLX implementation does appear to look for multi-sample buffers:

https://github.com/google/angle/blob/master/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp#L642

Is there an EGL implementation? I only see WGL, CGL and GLX.

What implementation runs on Ubuntu? I assume it would have been EGL, but now I am thinking it is GLX?

I am sorry I find ANGLE confusing.

bsergean commented 8 years ago

On Linux/CentOS it was the GLX code path that was active when I was using headless-gl last year.

On Wed, Jun 22, 2016 at 2:22 AM, Ben Houston (Clara.io) < notifications@github.com> wrote:

@joshunger https://github.com/joshunger, I find ANGLE a bit hard to understand, I'm still somewhat overwhelmed by it. I guess I am missing a high level overview of ANGLE. Do you know where one is?

The GLX implementation does appear to look for multi-sample buffers:

https://github.com/google/angle/blob/master/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp#L642

Is there an EGL implementation? I only see WGL, CGL and GLX.

What implementation runs on Ubuntu? I assume it would have been EGL, but now I am thinking it is GLX?

I am sorry I find ANGLE confusing.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/stackgl/headless-gl/issues/30#issuecomment-227726451, or mute the thread https://github.com/notifications/unsubscribe/ALTvUaYhFkPNX__w19boqV_yMn0Fnh_lks5qOSj4gaJpZM4F89kd .

joshunger commented 8 years ago

Has anyone done any work on this?

jlcastrillon91 commented 6 years ago

It is too late perhaps to ask this. Is there any solution to this problem or at least an alternative for threejs offscreen rendering with multisampling?

jlcastrillon91 commented 6 years ago

Just as @bsergean I managed to make it work for headles-gl but this time with threejs postprocessing by adapting one of their examples (ssaa). Here are some of the algorithms they provide: http://techbrood.com/threejs/examples/webgl_postprocessing_msaa.html https://threejs.org/examples/webgl_postprocessing_smaa.html https://threejs.org/examples/webgl_postprocessing_ssaa.html

Results are great but I guess slower than it could be if headless-gl could support it. I hope some day this wonderful library can handle antialiasing.

andrewvarga commented 1 year ago

I wanted to bump this to see if there have been any updates or other alternative libraries which allow working with the WebGL (prefrably WebGL 2) API in node.js but implement it on top of something that supports antialiasing, like ES 3? Postprocessing like FXAA, SSAA doesn't really work too well for my use case unfortunately..

bhouston commented 1 year ago

I think everyone just uses Headless Chrome these days.

On Wed, Apr 26, 2023 at 6:15 AM Andrew Varga @.***> wrote:

I wanted to bump this to see if there have been any updates or other alternative libraries which allow working with the WebGL (prefrably WebGL 2) API in node.js but implement it on top of something that supports antialiasing, like ES 3? Postprocessing like FXAA, SSAA doesn't really work too well for my use case unfortunately..

— Reply to this email directly, view it on GitHub https://github.com/stackgl/headless-gl/issues/30#issuecomment-1523169356, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEPV7K7CFFCXK36BPX2NIDXDDYTLANCNFSM4BPT3EOQ . You are receiving this because you were mentioned.Message ID: @.***>

-- Ben Houston, CTO https://threekit.com 613-762-4113 / http://calendly.com/benhouston3d

bhouston commented 1 year ago

Via Puppeteer or equivalents.

On Wed, Apr 26, 2023 at 10:26 AM Ben Houston @.***> wrote:

I think everyone just uses Headless Chrome these days.

On Wed, Apr 26, 2023 at 6:15 AM Andrew Varga @.***> wrote:

I wanted to bump this to see if there have been any updates or other alternative libraries which allow working with the WebGL (prefrably WebGL 2) API in node.js but implement it on top of something that supports antialiasing, like ES 3? Postprocessing like FXAA, SSAA doesn't really work too well for my use case unfortunately..

— Reply to this email directly, view it on GitHub https://github.com/stackgl/headless-gl/issues/30#issuecomment-1523169356, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEPV7K7CFFCXK36BPX2NIDXDDYTLANCNFSM4BPT3EOQ . You are receiving this because you were mentioned.Message ID: @.***>

-- Ben Houston, CTO https://threekit.com 613-762-4113 / http://calendly.com/benhouston3d

-- Ben Houston, CTO https://threekit.com 613-762-4113 / http://calendly.com/benhouston3d