jme3_shaders.adoc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. = JME3 and Shaders
  2. :author:
  3. :revnumber:
  4. :revdate: 2016/03/17 20:48
  5. :relfileprefix: ../../
  6. :imagesdir: ../..
  7. ifdef::env-github,env-browser[:outfilesuffix: .adoc]
  8. == Shaders Basics
  9. Shaders are sets of instructions that are executed on the GPU. They are used to take advantage of hardware acceleration available on the GPU for rendering purposes.
  10. This paper only covers Vertex and Fragment shaders because they are the only ones supported by JME3 for the moment. Be aware that there are some other types of shaders (geometry, tessellation,…).
  11. There are multiple frequently used languages that you may encounter to code shaders but as JME3 is based on OpenGL, shaders in JME use GLSL and any example in this paper will be written in GLSL.
  12. === How Does it work?
  13. To keep it Simple: The Vertex shader is executed once for each vertex in the view, then the Fragment shader (also called the Pixel shader) is executed once for each pixel on the screen.
  14. The main purpose of the Vertex shader is to compute the screen coordinate of a vertex (where this vertex will be displayed on screen) while the main purpose of the Fragment shader is to compute the color of a pixel.
  15. This is a very simplified graphic to describe the call stack:
  16. image:jme3/advanced/jme3andshaders.png[jme3andshaders.png,width="",height="", align="left]
  17. The main program sends mesh data to the vertex shader (vertex position in object space, normals, tangents, etc..). The vertex shader computes the screen position of the vertex and sends it to the Fragment shader. The fragment shader computes the color, and the result is displayed on screen or in a texture.
  18. === Variables scope
  19. There are different types of scope for variables in a shader:
  20. * uniform: User defined variables that are passed by the main program to the vertex and fragment shader, these variables are global for a given execution of a shader.
  21. * attribute: Per-vertex variables passed by the engine to the shader, like position, normal, etc (Mesh data in the graphic)
  22. * varying: Variables passed from the vertex shader to the fragment shader.
  23. There is a large panel of variable types to be used, for more information about it I recommend reading the GLSL specification link:http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf[here].
  24. === Spaces and Matrices
  25. To understand the coming example you must know about the different spaces in 3D computer graphics, and the matrices used to translate coordinate from one space to another.
  26. image:jme3/advanced/jme3andshaders-1.png[jme3andshaders-1.png,width="",height="", align="left"]
  27. The engine passes the object space coordinates to the vertex shader. We need to compute its position in projection space. To do that we transform the object space position by the WorldViewProjectionMatrix, which is a combination of the World, View, Projection matrices (who would have guessed?).
  28. === Simple example: rendering a solid color on an object
  29. Here is the simplest application to shaders, rendering a solid color.
  30. Vertex Shader:
  31. [source,java]
  32. ----
  33. //the global uniform World view projection matrix
  34. //(more on global uniforms below)
  35. uniform mat4 g_WorldViewProjectionMatrix;
  36. //The attribute inPosition is the Object space position of the vertex
  37. attribute vec3 inPosition;
  38. void main(){
  39. //Transformation of the object space coordinate to projection space
  40. //coordinates.
  41. //- gl_Position is the standard GLSL variable holding projection space
  42. //position. It must be filled in the vertex shader
  43. //- To convert position we multiply the worldViewProjectionMatrix by
  44. //by the position vector.
  45. //The multiplication must be done in this order.
  46. gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
  47. }
  48. ----
  49. Fragment Shader :
  50. [source,java]
  51. ----
  52. void main(){
  53. //returning the color of the pixel (here solid blue)
  54. //- gl_FragColor is the standard GLSL variable that holds the pixel
  55. //color. It must be filled in the Fragment Shader.
  56. gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
  57. }
  58. ----
  59. For example applying this shader to a sphere would render a solid blue sphere on screen.
  60. == How to use shaders in JME3
  61. You probably heard that JME3 is "`shader oriented`", but what does that mean? +
  62. Usually, to use shaders you must create a program. This program specifies the vertex shader and the fragment shader to use. JME3 encloses this in the material system. Every material in JME3 uses shaders.
  63. For example let’s have a look at the SolidColor.j3md file :
  64. [source,java]
  65. ----
  66. MaterialDef Solid Color {
  67. //This is the complete list of user defined uniforms to be used in the
  68. //shaders
  69. MaterialParameters {
  70. Vector4 Color
  71. }
  72. Technique {
  73. //This is where the vertex and fragment shader files are
  74. //specified
  75. VertexShader GLSL100: Common/MatDefs/Misc/SolidColor.vert
  76. FragmentShader GLSL100: Common/MatDefs/Misc/SolidColor.frag
  77. //This is where you specify which global uniform you need for your
  78. //shaders
  79. WorldParameters {
  80. WorldViewProjectionMatrix
  81. }
  82. }
  83. Technique FixedFunc {
  84. }
  85. }
  86. ----
  87. For more information on JME3 material system, I suggest you read link:https://hub.jmonkeyengine.org/t/jmonkeyengine3-material-system-full-explanation/12947[jMonkeyEngine3 material system - full explanation].
  88. === JME3 Global uniforms
  89. JME3 can expose pre-computed global uniforms to your shaders. You must specify the ones that are required for your shader in the WorldParameter's section of the material definition file (.j3md).
  90. [NOTE]
  91. ====
  92. In the shader, the uniform names will be prefixed by a “g_”.
  93. ====
  94. In the example above, WorldViewProjectionMatrix is declared as uniform mat4 g_WorldViewProjectionMatrix in the shader.
  95. The complete list of global uniforms that can be used in JME3 can be found in link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-core/src/main/java/com/jme3/shader/UniformBinding.java[UniformBinding.java].
  96. === JME3 Lighting Global uniforms
  97. JME3 uses some global uniforms for lighting:
  98. * g_LightDirection (vec4): the direction of the light
  99. ** use for SpotLight: x,y,z contain the world direction vector of the light, the w component contains the spotlight angle cosine
  100. * g_LightColor (vec4): the color of the light
  101. * g_LightPosition: the position of the light
  102. ** use for SpotLight: x,y,z contain the world position of the light, the w component contains 1/lightRange
  103. ** use for PointLight: x,y,z contain the world position of the light, the w component contains 1/lightRadius
  104. ** use for DirectionalLight: strangely enough it's used for the direction of the light…this might change though. The fourth component contains -1 and it's used in the lighting shader to know if it's a directionalLight or not.
  105. * g_AmbientLightColor: the color of the ambient light.
  106. These uniforms are passed to the shader without having to declare them in the j3md file, but you have to specify in the technique definition “ LightMode MultiPass see lighting.j3md for more information.
  107. === JME3 attributes
  108. Those are different attributes that are always passed to your shader.
  109. You can find a complete list of those attribute in the Type enum of the VertexBuffer in link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java[VertexBuffer.java].
  110. [NOTE]
  111. ====
  112. In the shader the attributes names will be prefixed by an “in”.
  113. ====
  114. When the enumeration lists some usual types for each attribute (for example texCoord specifies two floats) then that is the format expected by all standard JME3 shaders that use that attribute. When writing your own shaders though you can use alternative formats such as placing three floats in texCoord simply by declaring the attribute as vec3 in the shader and passing 3 as the component count into the mesh setBuffer call.
  115. === User's uniforms
  116. At some point when making your own shader you'll need to pass your own uniforms.
  117. Any uniform has to be declared in the material definition file (.j3md) in the “MaterialParameters section.
  118. [source,java]
  119. ----
  120. MaterialParameters {
  121. Vector4 Color
  122. Texture2D ColorMap
  123. }
  124. ----
  125. You can also pass some define to your vertex/fragment programs to know if an uniform as been declared.
  126. You simply add it in the Defines section of your Technique in the definition file.
  127. [source,java]
  128. ----
  129. Defines {
  130. COLORMAP : ColorMap
  131. }
  132. ----
  133. For integer and floating point parameters, the define will contain the value that was set.
  134. For all other types of parameters, the value 1 is defined.
  135. If no value is set for that parameter, the define is not declared in the shader.
  136. Those material parameters will be sent from the engine to the shader as follows,
  137. there are setXXXX methods for any type of uniform you want to pass.
  138. [source,java]
  139. ----
  140. material.setColor("Color", new ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); // red color
  141. material.setTexture("ColorMap", myTexture); // bind myTexture for that sampler uniform
  142. ----
  143. To use this uniform in the shader, you need to declare it in the .frag or .vert files (depending on where you need it).
  144. You can make use of the defines here and later in the code: *Note that the “m_ prefix specifies that the uniform is a material parameter.*
  145. [source,java]
  146. ----
  147. uniform vec4 m_Color;
  148. #ifdef COLORMAP
  149. uniform sampler2D m_ColorMap;
  150. #endif
  151. ----
  152. The uniforms will be populated at runtime with the value you sent.
  153. === Example: Adding Color Keying to the Lighting.j3md Material Definition
  154. Color Keying is useful in games involving many players. It consists of adding some player-specific color on models textures. The easiest way of doing this is to use a keyMap which will contain the amount of color to add in its alpha channel.
  155. We need to pass 2 new parameters to the Lighting.j3md definition, MaterialParameters section:
  156. [source,java]
  157. ----
  158. // Keying Map
  159. Texture2D KeyMap
  160. // Key Color
  161. Color KeyColor
  162. ----
  163. Below, add a new Define in the main Technique section:
  164. [source,java]
  165. ----
  166. KEYMAP : KeyMap
  167. ----
  168. In the Lighting.frag file, define the new uniforms:
  169. [source,java]
  170. ----
  171. #ifdef KEYMAP
  172. uniform sampler2D m_KeyMap;
  173. uniform vec4 m_KeyColor;
  174. #endif
  175. ----
  176. Further, when obtaining the diffuseColor from the DiffuseMap texture, check
  177. if we need to blend it:
  178. [source,java]
  179. ----
  180. #ifdef KEYMAP
  181. vec4 keyColor = texture2D(m_KeyMap, newTexCoord);
  182. diffuseColor.rgb = (1.0-keyColor.a) * diffuseColor.rgb + keyColor.a * m_KeyColor.rgb;
  183. #endif
  184. ----
  185. This way, a transparent pixel in the KeyMap texture doesn't modify the color. A black pixel replaces it for the m_KeyColor and values in between are blended.
  186. === Step by step
  187. * Create a vertex shader (.vert) file
  188. * Create a fragment shader (.frag) file
  189. * Create a material definition (j3md) file specifying the user defined uniforms, path to the shaders and the global uniforms to use
  190. * In your initSimpleApplication, create a material using this definition, apply it to a geometry
  191. * That’s it!!
  192. [source,java]
  193. ----
  194. // A cube
  195. Box box= new Box(Vector3f.ZERO, 1f,1f,1f);
  196. Geometry cube = new Geometry("box", box);
  197. Material mat = new Material(assetManager,"Path/To/My/materialDef.j3md");
  198. cube.setMaterial(mat);
  199. rootNode.attachChild(cube);
  200. ----
  201. === JME3 and OpenGL 3 & 4 compatibility
  202. GLSL 1.0 to 1.2 comes with built in attributes and uniforms (ie, gl_Vertex, gl_ModelViewMatrix, etc…). Those attributes are deprecated since GLSL 1.3 (opengl 3), hence JME3 global uniforms and attributes.
  203. Here is a list of deprecated attributes and their equivalent in JME3.
  204. [cols="2", options="header"]
  205. |===
  206. a|GLSL 1.2 attributes
  207. a|JME3 equivalent
  208. <a|gl_Vertex
  209. a|inPosition
  210. <a|gl_Normal
  211. a|inNormal
  212. <a|gl_Color
  213. a|inColor
  214. <a|gl_MultiTexCoord0
  215. a|inTexCoord
  216. <a|gl_ModelViewMatrix
  217. a|g_WorldViewMatrix
  218. <a|gl_ProjectionMatrix
  219. a|g_ProjectionMatrix
  220. <a|gl_ModelViewProjectionMatrix
  221. a|g_WorldViewProjectionMatrix
  222. <a|gl_NormalMatrix
  223. a|g_NormalMatrix
  224. |===
  225. === Useful links
  226. link:{attachmentsdir}/GLSL-ATI-Intro.pdf[GLSL-ATI-Intro.pdf]