CG实验6 交互与动画

1.实验目的和要求

  • 目的:了解交互与动画的基本思想,掌握交互与动画的常见实现方法;
  • 要求:读懂WebGL交互与动画示范代码,实现简单的交互与动画程序。

2. 实验过程

(1) 示范代码1为交互实例:在鼠标点击的位置上绘制出点;示范代码2为动画实例:三角形按照恒定的速度(45度/秒)旋转。结合示范代码,学习理解交互与动画的基本思想与实现; (2) 结合示范代码1,将示范代码2改为根据鼠标来控制三角形的旋转;

3.实验结果

示范代码1的结果如下图所示:

示范代码2如下图所示:

4.实验分析

请根据教材内容、网络资源及示范代码,简单分析下交互与动画的实现原理与方法。

5.实验代码

gl-matrix.js 下载地址:http://oty0nwcbq.bkt.clouddn.com/gl-matrix.js

(1) 鼠标点击交互绘点

(i) ClickedPoints.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Draw a point with a mouse click</title>
    </head>
    <script id="vertex-shader" type="x-shader/x-vertex">
         attribute vec4 a_Position;  
         void main() { 
           gl_Position = a_Position ; 
           gl_PointSize = 10.0; 
         } 
        </script>

        <script id="fragment-shader" type="x-shader/x-fragment">
             void main() { 
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
             } 
        </script>
    <body onload="startup()">
        <canvas id="myGLCanvas" width="600" height="600">
        </canvas>
    </body>
        <script type="text/javascript" src="gl-matrix.js"></script>
    <script type="text/javascript" src="ClickedPoints.js"></script>
</html>

(ii) ClickedPoints.js

var gl;
function startup(){
    var canvas = document.getElementById('myGLCanvas');//获取<canvas>元素
    gl = createGLContext(canvas);
    setupShaders(); 

 // // Get the storage location of a_Position
 var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
 if (a_Position < 0) {
   console.log('Failed to get the storage location of a_Position');
   return;
 }

 // Register function (event handler) to be called on a mouse press
 canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position); };

 // Specify the color for clearing <canvas>
 gl.clearColor(0.0, 0.0, 0.0, 1.0);

 // Clear <canvas>
 gl.clear(gl.COLOR_BUFFER_BIT);
 }

 var g_points = []; // The array for the position of a mouse press
 function click(ev, gl, canvas, a_Position) {
   var x = ev.clientX; // x coordinate of a mouse pointer
   var y = ev.clientY; // y coordinate of a mouse pointer
   var rect = ev.target.getBoundingClientRect() ;

   x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
   y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);
   // Store the coordinates to g_points array
   g_points.push(x); g_points.push(y);

   // Clear <canvas>
   gl.clear(gl.COLOR_BUFFER_BIT);

   var len = g_points.length;
   for(var i = 0; i < len; i += 2) {
     // Pass the position of a point to a_Position variable
     gl.vertexAttrib3f(a_Position, g_points[i], g_points[i+1], 0.0);

     // Draw
     gl.drawArrays(gl.POINTS, 0, 1);
   }
 }

function createGLContext(canvas) {
  var names = ["webgl", "experimental-webgl"];
  var context = null;
  for (var i=0; i < names.length; i++) {
    try {
      context = canvas.getContext(names[i]); //获取webgl context绘图上下文
    } catch(e) {}
    if (context) {
      break;
    }
  }
  if (context) {
    context.viewportWidth = canvas.width;
    context.viewportHeight = canvas.height;
  } else {
    alert("Failed to create WebGL context!");
  }
  return context;
}

function setupShaders() {    
  var vertexShader = loadShader(gl.VERTEX_SHADER, "vertex-shader");
  var fragmentShader = loadShader(gl.FRAGMENT_SHADER, "fragment-shader");

  var shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert("Failed to setup shaders");
  }

  gl.useProgram(shaderProgram);
  gl.program= shaderProgram;
}

function loadShader(type, ShaderId) {
  var shaderScript = document.getElementById( ShaderId );
    var shader = gl.createShader(type);
    gl.shaderSource( shader, shaderScript.text );
    gl.compileShader( shader );

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      alert("Error compiling shader" + gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);   
      return null;
  }
  return shader;  
}

(2) 三角形旋转动画

(i) RotatingTriangle.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Continually Rotate A Triangle</title>
    </head>
    <script id="vertex-shader" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        uniform mat4 u_ModelMatrix;  
        void main() { 
            gl_Position = u_ModelMatrix * a_Position; 
        } 
       </script>

       <script id="fragment-shader" type="x-shader/x-fragment">
            void main() { 
               gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
            } 
       </script>
    <body onload="startup()">
        <canvas id="myGLCanvas" width="600" height="600">
        </canvas>
    </body>
    <script type="text/javascript" src="gl-matrix.js"></script>
    <script type="text/javascript" src="RotatingTriangle.js">
    </script>
</html>

(ii) RotatingTriangle.js

var gl;
// Rotation angle (degrees/second)
var ANGLE_STEP = 45.0;
var modelMatrixI = mat4.create();
function startup(){
    var canvas = document.getElementById('myGLCanvas');//获取<canvas>元素
    gl = createGLContext(canvas);
    setupShaders(); 

  // Write the positions of vertices to a vertex shader
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the positions of the vertices');
    return;
  }

  // Specify the color for clearing <canvas>
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // Get storage location of u_ModelMatrix
  var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  if (!u_ModelMatrix) { 
    console.log('Failed to get the storage location of u_ModelMatrix');
    return;
  }

  // Current rotation angle
  var currentAngle = 0.0;
  // Model matrix
  var modelMatrix = mat4.create();

  // Start drawing
  var tick = function() {
    currentAngle = animate(currentAngle);  // Update the rotation angle
    draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix);   // Draw the triangle
    requestAnimationFrame(tick, canvas); // Request that the browser calls tick
  };
  tick();
 }

 function initVertexBuffers(gl) {
    var vertices = new Float32Array ([
      0, 0.5,   -0.5, -0.5,   0.5, -0.5
    ]);
    var n = 3;   // The number of vertices

    // Create a buffer object
    var vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
      console.log('Failed to create the buffer object');
      return -1;
    }

    // Bind the buffer object to target
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    // Write date into the buffer object
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    // Assign the buffer object to a_Position variable
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if(a_Position < 0) {
      console.log('Failed to get the storage location of a_Position');
      return -1;
    }
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    // Enable the assignment to a_Position variable
    gl.enableVertexAttribArray(a_Position);

    return n;
  }

  function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {
    // Set the rotation matrix
    //modelMatrix.setRotate(currentAngle, 0, 0, 1); // Rotation angle, rotation axis (0, 0, 1)
    var radian = Math.PI * currentAngle / 180.0; // Convert to radians
    mat4.rotate(modelMatrix, modelMatrixI, radian, [0, 0 , 1]);

    // Pass the rotation matrix to the vertex shader
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix);

    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Draw the rectangle
    gl.drawArrays(gl.TRIANGLES, 0, n);
  }

  // Last time that this function was called
  var g_last = Date.now();
  function animate(angle) {
    // Calculate the elapsed time
    var now = Date.now();
    var elapsed = now - g_last;
    g_last = now;
    // Update the current rotation angle (adjusted by the elapsed time)
    var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
    return newAngle %= 360;
  }

function createGLContext(canvas) {
  var names = ["webgl", "experimental-webgl"];
  var context = null;
  for (var i=0; i < names.length; i++) {
    try {
      context = canvas.getContext(names[i]); //获取webgl context绘图上下文
    } catch(e) {}
    if (context) {
      break;
    }
  }
  if (context) {
    context.viewportWidth = canvas.width;
    context.viewportHeight = canvas.height;
  } else {
    alert("Failed to create WebGL context!");
  }
  return context;
}

function setupShaders() {    
    var vertexShader = loadShader(gl.VERTEX_SHADER, "vertex-shader");
    var fragmentShader = loadShader(gl.FRAGMENT_SHADER, "fragment-shader");

    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Failed to setup shaders");
    }

    gl.useProgram(shaderProgram);
    gl.program= shaderProgram;
  }

  function loadShader(type, ShaderId) {
    var shaderScript = document.getElementById( ShaderId );
      var shader = gl.createShader(type);
      gl.shaderSource( shader, shaderScript.text );
      gl.compileShader( shader );

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert("Error compiling shader" + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);   
        return null;
    }
    return shader;  
  }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏静晴轩

精妙JS代码段搜集

现在到处都是JavaScript,倘若花点时间去体察,每次都能知道点新的东西。一旦你入了门,你总能从这里或是那里领悟到很多知识。一旦发现些许有意思的东西,总习惯...

35850
来自专栏彭湖湾的编程世界

&&运算符,三木运算符与React的条件渲染

在使用react框架的时候中往往会遇到需要条件渲染的情形,这时候,许多人会设想采用if语句来实现,比如下面,当满足条件condition时,conditonRe...

220110
来自专栏偏前端工程师的驿站

动手写个数字输入框2:起手式——拦截非法字符

前言  最近在用Polymer封装纯数字的输入框,开发过程中发现不是坑,也有不少值得研究的地方。本系列打算分4篇来叙述这段可歌可泣的踩坑经历: 《动手写个数字输...

22180
来自专栏java一日一条

Web性能优化系列:10个JavaScript性能提升的技巧

Nicholas Zakas是一位 JS 大师,Yahoo! 首页的前端主程。他是《高性能 Javascript》的作者,这本书值得每个程序员去阅读。

9920
来自专栏前端杂货铺

异步Promise实现

简介    异步回调的书写往往打乱了正常流的书写方式,在ECMAScript 6中实现了标准的Promise API,旨在 解决控制回调流程的问题。   简单的...

364100
来自专栏游戏杂谈

关于AS3的事件移除释疑

as3.0中的事件Event(位于包flash.events内,继承至Object,子类有…)

11820
来自专栏calmound

cocos2d-x 3.0 Node与Node层级结构

节点解释: 节点是场景图的基本元素。场景图的基本元素必须是节点对象或者是节点对象的子类。 其中主要可以看到Layer、MenuItem、Scene、Sprite...

29250
来自专栏图像识别与深度学习

《HTML5实战》Lesson04

Week05    2016/10/12上午1-4节 一、复习 二、Javascirpt 1,html5中使用内部javascript <script>可以...

29440
来自专栏专注 Java 基础分享

Java ---自定义标签(二)

     上篇文章的最后,我们自定义了一个带属性的标签,并使用它完成了一个简单的案例。其实到这我们已经可以看出来,前端jsp页面只需要写一个类似html语法的标...

21870
来自专栏更流畅、简洁的软件开发方式

[自定义服务器控件] 第二步:下拉列表框。

前面发了一个文本框的,这回发一个下拉列表框。 一般在写自定义控件之前都要考虑一下原来的控件(系统代的)有什么优缺点,有哪些功能是我想要的,但是自带的控件没有提...

31460

扫码关注云+社区

领取腾讯云代金券