//get shader sources with XMLHttpRequestObject var fs_source = null, vs_source = null; var xhr = new XMLHttpRequest(); //synchronous request requires a false third parameter xhr.open('GET', './shader.vs', false); //overriding the mime type is required xhr.overrideMimeType('text/xml'); xhr.send(null); if (xhr.readyState == xhr.DONE) { if(xhr.status === 200) { vs_source = xhr.responseXML.documentElement.firstChild.data; } else { console. error("Error: " + xhr.statusText); } } xhr.open('GET', './shader.fs', false); xhr.send(null); if (xhr.readyState == xhr.DONE) { if(xhr.status === 200) { fs_source = xhr.responseXML.documentElement.firstChild.data; } else { console. error("Error: " + xhr.statusText); } } In Listing 2-3 we are sending synchronous requests. Alternatively, we could use asynchronous calls and callback functions to signal that we are ready to move on with our program. In Listing 2-3 we also have to override the mime type to XML because the browser may not otherwise recognize the content of our shaders as a XML document. When the readyState is equal to XMLHttpRequestObject.DONE, then we check the status. A status of 200 means success and we can grab the data we need from the responseXML object: responseXML. documentElement.firstChild.data. If the status is not 200, we output an error message to the console. Starting with a copy of the 01/3D_triangles_depth_test.html file that was the last example of Chapter 1, remove the inline shader scripts at the top of the file and swap out these lines with those found in Listing 2-3: //get shader source var fs_source = document.getElementById('shader-fs').innerHTML, vs_source = document.getElementById('shader-vs').innerHTML; When you run the modified application, you will see that it works exactly the same as before. This can be found in the 02/vanilla_ajax.html file. N Note Remember that you must be running a web server to use this approach. In Figure 2-3, I naively try run- ning my file directly in the browser. You can see in the Chrome developer tools console the error that this causes. Developer and debugging tools are very useful and are covered in depth in Chapter 9. 40CHAPTER 2 N SHADERS 101 Figure 2-3. Error caused by trying to load an external file without a web server Using jQuery If we use a higher-level JavaScript API such as the very popular jQuery, there are a couple of advantages. First, it is easier because some of the low-level code is obscured for us. Second, it is more cross-browser compatible. A quick background of jQuery can be found in Appendix A. The equivalent jQuery functionality of Listing 2-3 is shown in Listing 2-4: Listing 2-4. Loading shaders with jQuery //get shader sources with jQuery Ajax $.ajax({ async: false, url: './shader.vs', success: function (data) { vs_source = data.firstChild.textContent; }, dataType: 'xml' }); $.ajax({ async: false, url: './shader.fs', success: function (data) { fs_source = data.firstChild.textContent; }, dataType: 'xml' }); Because jQuery extracts away the underlying XHR calls and the $.ajax method explicitly states the parameters that it is using, Listing 2-4 is both more concise and easier to understand than Listing 2- 3. Remember to also include a link to the jQuery library. The latest version of the jQuery library hosted on the jQuery CDN is available for development usage from http://code.jquery.com/jquery-latest.js and for production usage, the minified form can be found at http://code.jquery.com/jquery-latest.min.js. The full source code for this example is available on the books companion sites and is available in the file 02/jquery_ajax.html. There is an issue associated with including the full shader source, including script tags, externally and then parsing. The issue and solution are discussed in Chapter 9. 41CHAPTER 2 N SHADERS 101 GLSL Specification in More Detail Earlier, I mentioned that the GLSL is similar to C++. It uses a subset of ASCII characters and carriage return and/ or line feeds to terminate each line. The language is case sensitive and it is interesting to note that unlike C/C++, there are no character or string types used. As such, there are also no characters used for quoting. Variable and function names must start with an alphabet character or underscore, but cannot start with gl_ or be a reserved language word. Each shader program can have only one main method, which is the same as C/C++. Primitive Types The available basic types that are inherited from C++ are shown in Table 2-1. Table 2-1. C++ Inherited Types and Descriptions C++ types Description void Used to specify a function with no return value and/or no parameters bool Boolean true or false int Signed integers. Example: 1, 7, 13 float Floating point number. Example: 1.3, 7.0, 13.445 GLSL defines new primitive types that are shown in Table 2-2. Table 2-2. GLSL Types and Descriptions GLSL types Description vec2, vec3, vec4, ivec2, ivec3, vec4, bvec2, bvec3, bvec4 Vector of size 1×2, 1x3, or 1x4; and of type float, integer, or bool, respectively mat2, mat3, mat4 Floating point matrix of size 2x2, 3x3, or 4x4 sampler2D, samplerCube Handles to 2D or cube mapped textures We can also create structures that can hold more complex composite types. For instance: struct myStruct{ vec3 something; mat4 somethingElse; } Qualifiers GLSL has several optional qualifiers for variables. These fall into the categories of storage, parameter, precision and invariant qualifiers. Storage Qualifiers Storage qualifiers describe both the variable scope and relation to the WebGL program. 42CHAPTER 2 N SHADERS 101 A variable might be declared with attribute storage as attribute vec3 aColor;. Table 2-3. Storage Qualifiers Qualifier Description [none] The default for a variable is to have no storage qualifier. Local variables and function input parameters have no storage qualifiers. const Constant throughout the program. Read only. uniform Constant value across an entire primitive. attribute VS per vertex information from our WebGL application. varying VS write, FS read. N Note Prefixes are not required, but are commonly used to help represent the storage type of variables to other programmers: v for varying, u for uniform, and a for attribute. For example: attribute vec3 aVertexNormals; uniform uSampler; varying vOriginalPosition; Parameter Qualifiers Parameter qualifiers are used for function parameters (see Table 2-4). A function in WebGL might look like this: vec3 a = (0, 1, 0); vec3 c; void myFunction(a, out c){ c = a * 2; } Table 2-4. Parameter Qualifiers Qualifier Description [none] The default, which is the same thing as specifying the in qualifier In Parameters passed into a function Out Parameters to be passed out of a function, but were not initialized Inout Initialized parameter that will also be passed out of a function Precision Qualifiers There are three different precision qualifiers for the GLSL: highp, mediump, and lowp. highp satisfies the minimum requirements for the vertex language. mediump satisfies the minimum precision for the FS. lowp is less than medium but still fully represents the values of a color channel. 43CHAPTER 2 N SHADERS 101 Invariant Qualifier Lastly, there is the invariant qualifier