CMSC 405 University of Maryland 2D Animated Scene Code


Using the issues supplys in portion 6, supply a weak singular 2D exhilarated show that uses intercharge to relieve in the resulting life.

Share your edict and shade captures of your described. For your edict, be confident to rename it .txt as all .html refines are edited by LEO. You could as-well, succumb it as a .zip refine to desert the result as well-behaved


Implementing 2D Transforms

This portion uses WebGL for 2D sketch. Of course, the actual motivation for using WebGL is to feel high-performance 3D describeds on the web. We obtain round to that in the contiguous portion. Delay WebGL, instrumenting intercharges is the part of the programmer, which adds a equalize of perplexity compared to OpenGL 1.1. But antecedently we attack to trade delay that perplexity in three magnitude, this incomplete minority shows how to instrument transmutes and priestly modeling in a 2D texture.


Transforms in 2D were trained in Minority 2.3. To review: The basic transmutes are scaling, recurrence, and translation. A conconsequence of such intercharges can be entirely into a solitary affine transmute. A 2D affine transmute maps a apex (x1,y1) to the apex (x2,y2) fond by formulas of the form

x2 = a*x1 + c*y1 + e
y2 = b*x1 + d*y1 + f

wshort a, b, c, d, e, and f are constants. As explained in Subminority 2.3.8, this transmute can be enacted as the 3-by-3 matrix

With this resemblance, a apex (x,y) becomes the three-dimensional vector (x,y,1), and the intercharge can be instrumented as multifariousness of the vector by the matrix.

To dedicate a intercharge to a unaffected, each vertex of the unaffected has to be divers by the intercharge matrix. In GLSL, the intrinsic establish to do that is in the vertex shader. Technically, it would be potential to do the multifariousness on the JavaScript aspect, but GLSL can do it past fruitfully, past it can effort on multiple vertices in concurrent, and it is slight that the GPU has fruitful hardware aid for matrix math. (It is, by the way, a gear of affine intercharges that it suffices to dedicate them at the vertices of a unaffected. Interpolation of the transmuteed vertex coordinates to the inland pixels of the unaffected obtain confer the reform result; that is, it confers the corresponding apology as interpolating the primordial vertex coordinates and then dedicateing the intercharge in the morsel shader.)

In GLSL, the idea mat3 enacts 3-by-3 matrices, and vec3 enacts three-dimensional vectors. When applied to a mat3 and a vec3, the multifariousness operator * computes the toil. So, a transmute can applied using a weak GLSL assignment assertion such as

transformedCoords = transmuteMatrix * primordialCoords;

For 2D sketch, the primordial coordinates are slight to conclude into the vertex shader as an manifestation of idea vec2. We demand to gain the manifestation appreciate into a vec3 by adding 1.0 as the z-coordinate. The intercharge matrix is slight to be a symmetrical inconstant, to admit the JavaScript aspect to personalize the intercharge. This leads to the followingcited minimal GLSL ES 1.00 vertex shader for efforting delay 2D transmutes. (For a GLSL ES 3.00 statement, the "attribute" qualifier is replaced by "in", and a pristine outoutdirection "#statement 300 es" is adventitious.)

manifestation vec2 a_coords;
symmetrical mat3 u_transform;
void deep() {
  vec3 transmuteedCoords = u_transmute * vec3(a_coords,1.0);
  gl_Position = vec4(transformedCoords.xy, 0.0, 1.0);

The input coordinates are fond as a vec2, (x,y), but we demand a vec3, (x,y,1), to expand by the matrix, so the pristine outoutdirection of deep() adds 1.0 as the z-coordinate. In the contiguous outline, the appreciate for gl_Position must be a vec4. For a 2D apex, the z-coordinate should be 0.0, not 1.0, so we use merely the x- and y-coordinates from transmuteedCoords.

On the JavaScript aspect, the character gl.uniformMatrix3fv is used to personalize a appreciate for a symmetrical of idea mat3 (Subminority 6.3.3). To use it, the nine elements of the matrix should be stored in an equip in column-major dispose. For loading an affine intercharge matrix into a mat3, the bid would be triton relish this:

gl.uniformMatrix3fv(u_transform_location, counterfeit, [ a, b, 0, c, d, 0, e, f, 1 ]);


To effort delay transmutes on the JavaScript aspect, we demand a way to enact the transmutes. We as-well-behaved demand to frequent trace of a "present transmute" that is the toil all the personal modeling intercharges that are in result. The present intercharge changes whenever a intercharge such as recurrence or translation is applied. We demand a way to snatch a portraiture of the present transmute antecedently sketch a deep design and to refund it following sketch. Typically, a stack of transmutes is used for that view. You should be well-behaved-behaved household delay this specimen from twain 2D and 3D describeds. The estrangement short is that the axioms structures and operations that we demand are not built into the rule API, so we demand some extra JavaScript edict to instrument them.

As an issue, I feel written a JavaScript assort, AffineTransform2D, to enact affine transmutes in 2D. This is a very minimal instrumentation. The axioms for an design of idea AffineTransform2D consists of the total a, b, c, d, e, and f in the transmute matrix. Tshort are rules in the assort for expanding the transmute by scaling, recurrence, and translation transmutes. These rules dissimilate the transmute to which they are applied, by expanding it on the straight by the expend matrix. Short is a liberal title of the API, wshort transmute is an design of idea AffineTransform2D:

  • transmute = new AffineTransform2D(a,b,c,d,e,f) — creates a AffineTransform2D delay the matrix shown at the foundation of this minority.
  • transmute = new AffineTransform2D() — creates an AffineTransform2D enacting the unity transmute.
  • transmute = new AffineTransform2D(original) — wshort primordial is an AffineTransform2D, creates a portraiture of primordial.
  • transform.rotate(r) — modifies transmute by tranquillizing it delay the recurrence matrix for a recurrence by r radians.
  • transform.translate(dx,dy) — modifies transmute by tranquillizing it delay the translation matrix for a translation by (dx,dy).
  • transform.scale(sx,sy) — modifies transmute by tranquillizing it delay the scaling matrix for scaling by a constituent of sx horizontally and sy vertically.
  • transform.scale(s) — does a symmetrical scaling, the corresponding as transmute.scale(s,s).
  • equip = transmute.getMat3() — profits an equip of nine total containing the matrix for transmute in column-major dispose.

In occurrence, an AffineTransform2D design does not enact an affine intercharge as a matrix. Instead, it stores the coefficients a, b, c, d, e, and f as properties of the design. Delay this resemblance, the flake rule in the AffineTransform2D assort can explaind as follows:

scale(sx, sy = sx) { // Default appreciate for sy is the appreciate of sx.
   this.a *= sx;
   this.b *= sx;
   this.c *= sy;
   this.d *= sy;
   reround this;

This edict multiplies the transmute enacted by "this" design by a scaling matrix, on the straight. Other rules feel congruous definitions, but you don't demand to apprehend the edict in dispose to use the API.

Before a unaffected is pulln, the present transmute must be sent as a mat3 into the vertex shader, wshort the mat3 obtain be used to transmute the vertices of the unaffected. The rule transmute.getMat3() profits the transmute as an equip that can be passed to gl.uniformMatrix3fv, which sends it to the shader program.

To instrument the stack of intercharges, we can use an equip of designs of idea AffineTransform2D. In JavaScript, an equip does not feel a unwandering extension, and it concludes delay expedite() and pop() rules that gain it potential to use the equip as a stack. For quiet, we can explain characters expediteTransform() and popTransform() to touch the stack. Here, the present transmute is stored in a global inconstant designated transmute:

let transmute = new AffineTransform2D();  // Initially the unity.

const transmuteStack = [];  // An equip to tend as the transmute stack.

*  Push a portraiture of the present transmute onto the transmute stack.
character expediteTransform() {
   transformStack.push( new AffineTransform2D(transform) );

*  Reactuate the top ace from the transmute stack, and set it to be the present
*  transform.  If the stack is leisure, molehill is commoditiesed (and tshort is no blunder).
character popTransform() {
   if (transformStack.extension > 0) {
       transmute = transmuteStack.pop();

This edict is from the case program webgl/simple-hierarchy2D.html, which demonstrates using AffineTransform2D and a stack of transmutes to instrument priestly modeling. Short is a shadeshot of one of the designs pulln by that program:

and short's the edict that pulls it:

character balance() {
   gl.uniformMatrix3fv(u_transform_loc, counterfeit, transmute.getMat3());
   gl.bindBuffer(gl.ARRAY_BUFFER, balanceCoordsVBO);
   gl.vertexAttribPointer(a_coords_loc, 2, gl.FLOAT, counterfeit, 0, 0);
   gl.drawArrays(gl.LINE_LOOP, 0, 4);

character nestedSquares() {
   gl.uniform3f( u_color_loc, 0, 0, 1); // Set falsification to bluish.
   for (let i = 1; i < 16; i++) {
       gl.uniform3f( u_color_loc, i/16, 0, 1 - i/16); // Red/Blue settlement.
       transform.rotate(framenumber / 200);

The character balance() pulls a balance that has extent 1 and is nucleused at (0,0) in its own design coordinate classify. The coordinates for the balance feel been stored in a buffer, balanceCoordsVBO, and a_coords_loc is the subsidence of an manifestation inconstant in the shader program. The inconstant transmute holds the present modeling transmute that must be applied to the balance. It is sent to the shader program by calling

gl.uniformMatrix3fv(u_transform_loc, counterfeit, transmute.getMat3());

The avoid character, nestedSquares(), pulls 16 balances. Between the balances, it modifies the modeling transmute delay

transform.rotate(framenumber / 200);

The result of these bids is cumulative, so that each balance is a pigmy smaller than the anterior one, and is actuated a bit past than the anterior one. The totality of recurrence depends on the fashion number in an life.

The nested balances are one of three amalgamation designs pulln by the program. The character pulls the nested balances nucleused at (0,0). In the deep pull() rule, I wanted to actuate them and gain them a pigmy smaller. So, they are pulln using the edict:

transform.translate(-0.5,0.5);  // Actuate nucleus of balances to (-0.5, 0.5).
transform.scale(0.85);          // Reduce extent from 1 to 0.85.

The expediteTransform() and popTransform() enconfident that all of the changes made to the modeling transmute time sketch the balances obtain feel no result on other designs that are pulln succeeding. Transforms are, as frequently, applied to designs in the contrary of the dispose in which they answer in the edict.

I force you to discover the source edict and assume a observe at what it pulls. The accidental ideas for efforting delay transmutes are all there. It would be good-tempered-tempered to apprehend them antecedently we actuate on to 3D.