Difference between revisions of "How to write cross GLProfile compatible shader using JOGL"

From JogampWiki
Jump to navigation Jump to search
(initial version (thx to Xerxes Ranby for starting it on forum))
 
(GLSL Version >= 130)
 
(4 intermediate revisions by the same user not shown)
Line 14: Line 14:
 
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/javax/media/opengl/GLContext.html#getGLSLVersionNumber%28%29 GLContext's getGLSLVersionNumber()], which returns the GLSL <code>VersionNumber</code> matching the GLContext's OpenGL version.
 
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/javax/media/opengl/GLContext.html#getGLSLVersionNumber%28%29 GLContext's getGLSLVersionNumber()], which returns the GLSL <code>VersionNumber</code> matching the GLContext's OpenGL version.
 
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/javax/media/opengl/GLContext.html#getGLSLVersionString%28%29 GLContext's getGLSLVersionString()], which returns the ''GLSL version directive'' matching the GLContext's OpenGL version.
 
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/javax/media/opengl/GLContext.html#getGLSLVersionString%28%29 GLContext's getGLSLVersionString()], which returns the ''GLSL version directive'' matching the GLContext's OpenGL version.
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#addGLSLVersion%28javax.media.opengl.GL2ES2%29 ShaderCode's addGLSLVersion(..)], which adds the matching  ''GLSL version directive'' to the shader code.
+
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#addGLSLVersion%28javax.media.opengl.GL2ES2%29 ShaderCode's addGLSLVersion(..)], which adds the matching  ''GLSL version directive'' to the shader code.
  
 
== GLSL Precision Directive ==
 
== GLSL Precision Directive ==
Line 22: Line 22:
 
Users may either add such ''precision directive'' in their code in a fine grained manner adjusting precision to their tasks,
 
Users may either add such ''precision directive'' in their code in a fine grained manner adjusting precision to their tasks,
 
or they may utilize a JOGL's feature:
 
or they may utilize a JOGL's feature:
* [{SERVER}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#addDefaultShaderPrecision%28javax.media.opengl.GL2ES2,%20int%29 ShaderCode's addDefaultShaderPrecision(..)], which adds ''default precision directive'' to the shader code.
+
* [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#addDefaultShaderPrecision%28javax.media.opengl.GL2ES2,%20int%29 ShaderCode's addDefaultShaderPrecision(..)], which adds ''default precision directive'' to the shader code.
  
 
== GLSL Version &amp; Default Precision Directive ==
 
== GLSL Version &amp; Default Precision Directive ==
 
The ''GLSL version and default precision'' directives maybe performed by the  
 
The ''GLSL version and default precision'' directives maybe performed by the  
[{SERVER}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html ShaderCode] class,
+
[{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html ShaderCode] class,
which can load your code from an input stream (URI source) or CharSequence array and customize it appropriately via [{SERVER}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#defaultShaderCustomization%28javax.media.opengl.GL2ES2,%20boolean,%20boolean%29 defaultShaderCustomization(..)].
+
which can load your code from an input stream (URI source) or CharSequence array and customize it appropriately via [{{SERVER}}/deployment/jogamp-current/javadoc/jogl/javadoc/com/jogamp/opengl/util/glsl/ShaderCode.html#defaultShaderCustomization%28javax.media.opengl.GL2ES2,%20boolean,%20boolean%29 defaultShaderCustomization(..)].
  
 
<code>ShaderCode</code> also allows users to perform further custom editing ''on the fly''.
 
<code>ShaderCode</code> also allows users to perform further custom editing ''on the fly''.
  
 
Have a look at how GLSL shaders are loaded inside the jogamp jogl junit tests using <code>ShaderCode</code> for cross platform GLSL use:
 
Have a look at how GLSL shaders are loaded inside the jogamp jogl junit tests using <code>ShaderCode</code> for cross platform GLSL use:
[{SERVER}/git/?p=jogl.git;a=blob;f=src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java;hb=HEAD#l91 RedSquareES2].
+
[{{SERVER}}/git/?p=jogl.git;a=blob;f=src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/RedSquareES2.java;hb=HEAD#l91 RedSquareES2].
  
=== GLSL Version Compatibility ===
+
<pre>
==== GLSL Version >= 130 ====
+
final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), "shader", "shader/bin", "RedSquareShader", true);
 +
final ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), "shader", "shader/bin", "RedSquareShader", true);
 +
vp0.defaultShaderCustomization(gl, true, true);
 +
fp0.defaultShaderCustomization(gl, true, true);
 +
final ShaderProgram sp0 = new ShaderProgram();
 +
sp0.add(gl, vp0, System.err);
 +
sp0.add(gl, fp0, System.err);
 +
</pre>
 +
 
 +
== GLSL Version Compatibility ==
 +
=== GLSL Version >= 130 ===
 
GLSL version >= 130 uses in and out keywords instead of attributes and varying.   
 
GLSL version >= 130 uses in and out keywords instead of attributes and varying.   
 
it also requires the fragment shader to explicitly define the out variable.  
 
it also requires the fragment shader to explicitly define the out variable.  
Line 53: Line 63:
 
   #define varying in
 
   #define varying in
 
   out vec4 mgl_FragColor;
 
   out vec4 mgl_FragColor;
 +
  #define texture2D texture
 
  #else
 
  #else
 
   #define mgl_FragColor gl_FragColor   
 
   #define mgl_FragColor gl_FragColor   
Line 60: Line 71:
 
Look at the Cross platform GLSL shaders used by the JogAmp JOGL junit tests how to add the pre-processor defines:
 
Look at the Cross platform GLSL shaders used by the JogAmp JOGL junit tests how to add the pre-processor defines:
 
  [{{SERVER}}/git/?p=jogl.git;a=tree;f=src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader;hb=HEAD JOGL Unit Test Shader].
 
  [{{SERVER}}/git/?p=jogl.git;a=tree;f=src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader;hb=HEAD JOGL Unit Test Shader].
 +
 +
=== GLSL ES Version 1.0 ===
 +
==== Attribute Types ====
 +
ES2. aka ''GLSL ES version 1.0'', has restrictions on the types that can be used as attributes. Specifically:
 +
 +
''GLSL ES version 1.0'' spec, section 4.3.3:
 +
<blockquote>
 +
The attribute qualifier can be used only with the data types float, vec2, vec3, vec4, mat2, mat3, and
 +
mat4. Attribute variables cannot be declared as arrays or structures.
 +
</blockquote>
 +
 +
==== Multiple Render Targets (MRT) ====
 +
Also, ''GLSL ES version 1.0'' doesn't support ''Multiple Render Targets (MRT)''.
 +
 +
''GLSL version 1.30'' MRT code snippet:
 +
<pre>
 +
out vec4 mgl_FragData[2];
 +
varying vec4    frontColor;
 +
 +
void main (void) {
 +
    mgl_FragData[0] = vec4( frontColor.r, 0.0, 0.0, 1.0 );
 +
    mgl_FragData[1] = vec4( 0.0, frontColor.g, 0.0, 1.0 );
 +
}
 +
</pre>
 +
 +
''GLSL ES version 1.0'' restricts the number of possible framebuffer color attachments to one,
 +
hence any shader using multiple outputs need to be rewritten utilizing multiple shader passes.
 +
 +
'''To be refined''', maybe remove lower part.
 +
 +
In ''GLSL version <= 1.20'', writing to multiple fragment shader outputs meant writing
 +
to gl_FragData[0 .. n]. This will still work in ''GLSL ES version 1.0'', but the gl_FragData array will consist of only one
 +
element. In 1.30, outputs have to be declared as "out" parameters and then associated with
 +
a specific "location" with glBindFragDataLocation. In GLSL 1.40 and above, "out" parameters can be assigned
 +
specific locations with the location directive. For example: "layout(location = 0) out vec4 output". In ES3,
 +
the situation is the same as ''GLSL version 1.40'' and above.
 +
 +
==== Texture Formats ====
 +
glTexImage* requires that the "format" and "internalformat" parameters must match, and can only be one of
 +
GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA. This is incompatible with every other
 +
OpenGL version.
 +
 +
==== No ''read'' Framebuffer ====
 +
'''To be refined'''
 +
A complete lack of any concept of "read" framebuffers; the result is actually a subset of 3.0 and the four
 +
framebuffer extensions JOGL uses to give equivalent behaviour on 2.1 (GL_ARB_framebuffer_object |  GL_EXT_framebuffer_object, GL_EXT_framebuffer_multisample, GL_EXT_framebuffer_blit, GL_EXT_packed_depth_stencil).
 +
 +
An amusing one is the stencil buffer: The majority of implementations don't support separate depth and stencil
 +
framebuffer attachments, so you're required to use GL_OES_packed_depth_stencil to actually get a stencil buffer
 +
at all (you're certainly not going to be doing any significant work rendering without a depth buffer).

Latest revision as of 18:17, 11 June 2014

Note: To be refined / edited!

In order to create cross platform GLSL shaders the following three points have to be taken into account:

GLSL Version Directive

Some OpenGL implementations require the GLSL version directive #version <number> to be explicitly included in the shader source as the first directive.

The GLSL version directive shall match the OpenGL version of the context, see GLContext's GLSLVersionNumber.

If any of above requirements are not fulfilled the shader code compilation might fail on certain platforms and OpenGL version.

Users may compose their shader at runtime to comply with the GLSL version directive utilizing JOGL's features:

GLSL Precision Directive

In case you target ES2, ES3 or >= GL3 OpenGL contexts one needs to add a precision qualifier to your variable declarations or use a shader wide default precision qualifier directive.

Users may either add such precision directive in their code in a fine grained manner adjusting precision to their tasks, or they may utilize a JOGL's feature:

GLSL Version & Default Precision Directive

The GLSL version and default precision directives maybe performed by the ShaderCode class, which can load your code from an input stream (URI source) or CharSequence array and customize it appropriately via defaultShaderCustomization(..).

ShaderCode also allows users to perform further custom editing on the fly.

Have a look at how GLSL shaders are loaded inside the jogamp jogl junit tests using ShaderCode for cross platform GLSL use: RedSquareES2.

final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), "shader", "shader/bin", "RedSquareShader", true);
final ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), "shader", "shader/bin", "RedSquareShader", true);
vp0.defaultShaderCustomization(gl, true, true);
fp0.defaultShaderCustomization(gl, true, true);
final ShaderProgram sp0 = new ShaderProgram();
sp0.add(gl, vp0, System.err);
sp0.add(gl, fp0, System.err);

GLSL Version Compatibility

GLSL Version >= 130

GLSL version >= 130 uses in and out keywords instead of attributes and varying. it also requires the fragment shader to explicitly define the out variable. Compatibility with previous GLSL versions can be reached by adding some GLSL pre-processor defines at the beginning of the shaders.

Vertex shader:

#if __VERSION__ >= 130
   #define attribute in
   #define varying out
#endif

Fragment shader:

#if __VERSION__ >= 130
   #define varying in
   out vec4 mgl_FragColor;
   #define texture2D texture
 #else
   #define mgl_FragColor gl_FragColor  
#endif

Look at the Cross platform GLSL shaders used by the JogAmp JOGL junit tests how to add the pre-processor defines:

JOGL Unit Test Shader.

GLSL ES Version 1.0

Attribute Types

ES2. aka GLSL ES version 1.0, has restrictions on the types that can be used as attributes. Specifically:

GLSL ES version 1.0 spec, section 4.3.3:

The attribute qualifier can be used only with the data types float, vec2, vec3, vec4, mat2, mat3, and mat4. Attribute variables cannot be declared as arrays or structures.

Multiple Render Targets (MRT)

Also, GLSL ES version 1.0 doesn't support Multiple Render Targets (MRT).

GLSL version 1.30 MRT code snippet:

out vec4 mgl_FragData[2];
varying vec4    frontColor;

void main (void) {
    mgl_FragData[0] = vec4( frontColor.r, 0.0, 0.0, 1.0 ); 
    mgl_FragData[1] = vec4( 0.0, frontColor.g, 0.0, 1.0 );
}

GLSL ES version 1.0 restricts the number of possible framebuffer color attachments to one, hence any shader using multiple outputs need to be rewritten utilizing multiple shader passes.

To be refined, maybe remove lower part.

In GLSL version <= 1.20, writing to multiple fragment shader outputs meant writing to gl_FragData[0 .. n]. This will still work in GLSL ES version 1.0, but the gl_FragData array will consist of only one element. In 1.30, outputs have to be declared as "out" parameters and then associated with a specific "location" with glBindFragDataLocation. In GLSL 1.40 and above, "out" parameters can be assigned specific locations with the location directive. For example: "layout(location = 0) out vec4 output". In ES3, the situation is the same as GLSL version 1.40 and above.

Texture Formats

glTexImage* requires that the "format" and "internalformat" parameters must match, and can only be one of GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA. This is incompatible with every other OpenGL version.

No read Framebuffer

To be refined A complete lack of any concept of "read" framebuffers; the result is actually a subset of 3.0 and the four framebuffer extensions JOGL uses to give equivalent behaviour on 2.1 (GL_ARB_framebuffer_object | GL_EXT_framebuffer_object, GL_EXT_framebuffer_multisample, GL_EXT_framebuffer_blit, GL_EXT_packed_depth_stencil).

An amusing one is the stencil buffer: The majority of implementations don't support separate depth and stencil framebuffer attachments, so you're required to use GL_OES_packed_depth_stencil to actually get a stencil buffer at all (you're certainly not going to be doing any significant work rendering without a depth buffer).