webgl icon indicating copy to clipboard operation
webgl copied to clipboard

Add support for GLSL dFdx, dFdy, and fwidth

Open ianmackenzie opened this issue 8 years ago • 3 comments

These 'standard derivative' functions are very useful for a variety of techniques such as edge detection and blurring without requiring a second rendering pass. They seem to have very good support across most browsers:

I believe this change should be safe even on browsers that don't support the extension (not sure how to test this!), since getExtension() simply returns null if the extension is not available. Additionally, individual fragment shaders must still explicitly enable the extension by adding

#extension GL_OES_standard_derivatives : enable

as the first line, so there is a strong hint to the shader writer that using these functions may not work on all browsers.

Unfortunately Elm's GLSL parser doesn't seem to recognize #extension as valid syntax; ideally that would be fixed in the upstream Haskell package, but in the meantime the workaround is to use unsafeShader.

Update: I've added standardDerivatives as an Option that must be explicitly enabled, since there's at least one report of extension-loading having measurable performance impact.

As mentioned in the documentation I added for WebGL.standardDerivatives, it's possible to detect the presence or absence of the extension using #ifdef GL_OES_standard_derivatives in the fragment shader to conditionally compile different fragment shader code (example).

As far as I can tell, calling getExtension() to attempt to enable a non-available extension (or enabling one that is then not used) has no effect other than the time taken to call getExtension(). Shaders will only fail to compile (potential runtime error) if dFdx etc. is used outside of an #ifdef GL_OES_standard_derivatives block, on a browser where standard derivatives are not supported.

What to do about [glsl|...|] parsing?

Right now, as mentioned above, Elm's GLSL parser doesn't recognize #extension lines (or even #ifdef, actually) so any shader using them has to be created with unsafeShader, which is messy. Additionally, if uniforms, attributes or varyings were added within #ifdef blocks then the inferred Elm type of the shader would change depending on the presence of the extension! Some potential solutions:

  • Update the GLSL parser to recognize #extension and #ifdef, and disallow declaration of uniforms, attributes or varyings inside an #ifdef. Most flexible, but requires the most work. (This is the 'perfect world' that the current WebGL.standardDerivatives documentation text assumes...)
  • Keep the GLSL parser as-is, and internally prepend "#extension GL_OES_standard_derivatives : enable\n" to the resulting shader string if the StandardDerivatives option has been enabled. This way the shader writer doesn't have to remember to add the #extension line to their shaders (just call WebGL.toHtmlWith with WebGL.standardDerivatives as one of the options, then go ahead and start using dFdx etc.). Since the GLSL parser also doesn't seem to support #ifdef, I think in this case the approach would be "if you want to use dFdx and friends, just realize that it may not work cross-browser". (It seems to me, though, that for practical purposes it's pretty safe to assume that the extension is available anywhere that WebGL is.) If people really want to support both cases, then there's always unsafeShader...
  • Don't support the standard derivatives at all ☹️

ianmackenzie avatar Jul 28 '17 15:07 ianmackenzie

I just came to try use this extension in one of my shaders and found that I couldn't. Currently I am just exploring WebGL. The particular thing I was trying to do was calculate the face normals in the fragment shader for flat shading. I am using glslify to write modular glsl and use other libs from npm. In this particular case I wanted to use glsl-face-normal to calculate the normals from a position. Unfortunately this requires this extension and therefore is currently incompatible with elm.

RGBboy avatar Dec 27 '17 21:12 RGBboy

If memory serves correctly VSM shadows (low cost, soft, dynamic shadows) require standard derivatives as well.

DrBearhands avatar Dec 28 '17 16:12 DrBearhands

It should be possible to support this feature when we replace the glsl parser in the Elm compiler.

This won't make it into Elm 0.19, but will be considered later.

w0rm avatar Mar 04 '18 16:03 w0rm