elm-community / webgl

Moved to elm-explorations/webgl
https://package.elm-lang.org/packages/elm-explorations/webgl/latest
BSD 3-Clause "New" or "Revised" License
131 stars 18 forks source link

Add support for GLSL dFdx, dFdy, and fwidth #48

Open ianmackenzie opened 7 years ago

ianmackenzie commented 7 years ago

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:

RGBboy commented 6 years ago

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.

DrBearhands commented 6 years ago

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

w0rm commented 6 years ago

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.