Search code examples
maththree.jsopengl-esglslwebgl

Convert fragment shader gradient result from black to transparent in GL ES


I'm trying to generate realistic stars for an open source game I'm working on. I'm generating the stars using principles covered here. I'm using the three.js library in a Chromium engine (NW.js). The problem I've found is that the star glow fades into black instead of into transparency.

Whilst it looks nice for single star,

enter image description here

multiple stars have a serious problem:

enter image description here


My code is as follows:

Vertex shader

attribute vec3 glow;
varying vec3 vGlow;

void main() {
  vGlow = glow;
  vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
  gl_PointSize = 100.0;
  gl_Position = projectionMatrix * mvPosition;
}

Fragment shader

varying vec3 vGlow;

void main() {
  float starLuminosity  = 250.0;
  float invRadius = 60.0;
  float invGlowRadius = 2.5;

  // Get position relative to center.
  vec2 position = gl_PointCoord;
  position.x -= 0.5;
  position.y -= 0.5;

  // Airy disk calculation.
  float diskScale = length(position) * invRadius;
  vec3 glow = vGlow / pow(diskScale, invGlowRadius);
  glow *= starLuminosity;

  gl_FragColor = vec4(glow, 1.0);
}

I've tried discarding pixels that are darker, but this does not solve the problem, it only hides it a tad:

if (gl_FragColor.r < 0.1 && gl_FragColor.g < 0.1 && gl_FragColor.b < 0.1) {
  discard;
}

enter image description here

The actual effect I'm after is as follows,

enter image description here

but I have no idea how to achieve this.

Any advice will be appreciated.


Solution

  • You cannot achieve this effect in the fragment shader because you are rendering multiple meshes or primitives. You have to enable Blending before rendering the geometry:

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    

    Also make sure that the Depth test is disabled.

    Additionally you must set the alpha channel from. e.g.:

    gl_FragColor = vec4(glow, 1.0);

    vec4(glow, (glow.r+glow.g.+glow.b)/3.0 * 1.1 - 0.1);