图形学实验一

Yunxiao 2020年12月15日 305次浏览

实验内容

本次实验的目的是对三角形和正方形做扭曲与拉伸的变形.

完整代码

html部分代码

<!DOCTYPE html>
<html>
<head>
  <link href='http://fonts.googleapis.com/css?family=Nixie+One|Open+Sans:300italic,300,700|Pacifico' rel='stylesheet' type='text/css'>
	<meta charset="UTF-8">
  <title> Experiment 1 | Coursera WebGL</title>
  <link rel="stylesheet" href="../../css/reset.css">
  <link rel="stylesheet" href="../../css/styles.css">
  <link rel="stylesheet" href="../../css/assignment.css">
  <script id="vertex-shader" type="x-shader/x-vertex">
  attribute vec4 vPosition;

  void
  main()
  {
    gl_Position = vPosition;
  }
  </script>

  <script id="fragment-shader" type="x-shader/x-fragment">
  precision mediump float;

  void
  main()
  {
    gl_FragColor = vec4(0.28, 0.32, 0.84, 1);
  }
  </script>

  <script type="text/javascript" src="../../Common/webgl-utils.js"></script>
  <script type="text/javascript" src="../../Common/initShaders.js"></script>
  <script type="text/javascript" src="../../Common/MV.js"></script>
  <script type="text/javascript" src="experiment1.js"></script>
</head>

<body class="assignment">

  <!--[if lt IE 9]>
    <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
  <![endif]-->


  <header>
    <h1>第一次实验</h1>
    <h2>扭转 Twist &amp; 拉伸 Tasselation</h2>
  </header>

  <section class="settings">
    <h1>设置 Settings</h1>
    <form id="settings" class="settings-form">
      <div class="form-group">
        <label for="theta">Degrees</label>
        <input type=range id=theta min=0 value=60 max=360 step=15 oninput="outputThetaValue(value)">
        <output for=theta id=thetaValue>60</output>
      </div>
      <div class="form-group">
        <label for="numDivisions">Divisions</label>
        <input type=range id=numDivisions min=0 value=4 max=7 step=1 oninput="outputNumDivisionsValue(value)">
        <output for=numDivisions id=numDivisionsValue>4</output>
      </div>
      <div class="form-group">
        <label for="fill">Fill</label>
        <input type="radio" id="fillSolid" name="fill" value="solid" checked>Solid
        <input type="radio" id="fillMesh" name="fill" value="mesh">Mesh
      </div>
      <div class="form-group">
        <label for="shape">Shape</label>
        <input type="radio" id="triangleShape" name="shape" value="triangle" checked>Triangle
        <input type="radio" id="squareShape" name="shape" value="square">Square
      </div>
      <div id="gasketGroup" class="form-group">
        <label for="gasket">Gasket</label>
        <input type="checkbox" id="gasket" name="gasket" value="gasket">
      </div>
      <div class="form-group">
        <input id="reset" type="button" value="Reset">
      </div>
    </form>
  </section>

  <section class="drawing">
    <canvas id="gl-canvas" width="600" height="600">
      Oops ... your browser doesn't support the HTML5 canvas element
    </canvas>
  </section>

  <script>
  var outputThetaValue = function(value) {
    document.querySelector('#thetaValue').value = value;
  };
  var outputNumDivisionsValue = function(value) {
    document.querySelector('#numDivisionsValue').value = value;
  };
  </script>

</body>
</html>

js部分代码

'use strict';

var gl;
var program;
var points = [];
var numDivisions = 4;
var _gasket = false;
var _initialRotation = 60;
var _fill = 'solid';

// http://math.stackexchange.com/questions/240169/how-to-find-the-other-vertices-of-an-equilateral-triangle-given-one-vertex-and-c
// equilateral triangle centered about the origin

// 最初的三角形
var originalTriangle = [
  vec2(-(Math.sqrt(3)/2), -0.5),   // bottom left
  vec2(0, 1),                      // top middle
  vec2((Math.sqrt(3)/2), -0.5)     // bottom right
];

// 最初的正方形是由两个三角形拼成的
var originalSquare = [
  vec2(-0.5, -0.5),
  vec2(0.5, -0.5),
  vec2(0.5, 0.5),
  vec2(0.5, 0.5),
  vec2(-0.5, 0.5),
  vec2(-0.5, -0.5),
];

var resetPoints = function() {
  points = [];
};

var addTriangle = function(bottomLeft, topMiddle, bottomRight) {
  points.push(bottomLeft, topMiddle, bottomRight);
};

// 计算两个顶点的中间位置
var calculateMidPoint = function(vec2PointA, vec2PointB) {
  return mix(vec2PointA,vec2PointB,0.5);
};

// 计算一个顶点到坐标原点的距离
var calculateDistance = function(vec2Point) {
  return Math.sqrt(vec2Point[0] * vec2Point[0] + vec2Point[1] * vec2Point[1]);
};

// 递归细分三角形, 需要调用calculateMidPoint()函数.
// 注意, 如果使用者想要gasket衬垫, 则不填充中间的三角形.
var divideTriangle = function(a, b, c, count) {

  if ( count === 0 ) {
    addTriangle( a, b, c );
  }
  else {

      //bisect the sides

      var ab = calculateMidPoint( a, b);
      var ac = calculateMidPoint( a, c);
      var bc = calculateMidPoint( b, c);

      --count;

      // three new triangles
      if(document.getElementById('gasket').checked == false){
        divideTriangle( a, ab, ac, count );
        divideTriangle( c, ac, bc, count );
        divideTriangle( b, bc, ab, count );
      }
      else{
        divideTriangle( a, ab, ac, count );
        divideTriangle( c, ac, bc, count );
        divideTriangle( b, bc, ab, count );
        divideTriangle( ab,ac,bc,count );
      }
  }
};

var addSquare = function(a, b, c, e) {
  points.push(a, b, c);
  points.push(c, e, a);
};

// 递归细分正方形, 需要调用calculateMidPoint()函数.
var divideSquare = function(a, b, c, e, count) {
  // 在这里添加你的源代码
  //
  if ( count === 0 ) {
    addSquare( a, b, c ,e);
  }
  else {
       
      //bisect the sides

      var ab = calculateMidPoint( a, b);
      var bc = calculateMidPoint( b, c);
      var cd = calculateMidPoint( c, e);
      var ae = calculateMidPoint( a, e);

      --count;

      // three new triangles

      divideSquare( ab, bc, cd, ae ,count);
      addSquare( a, b, c ,e);
  }
};

var render = function() {
  gl.clear( gl.COLOR_BUFFER_BIT );
  if (_fill === 'solid') {
    gl.drawArrays( gl.TRIANGLES, 0, points.length );
  }
  if (_fill === 'mesh') {
    for (var i=0; i<points.length; i+=3) {
      gl.drawArrays( gl.LINE_LOOP, i, 3);
    }
  }
};

var loadBuffer = function(data) {
  // load data onto gpu
  var bufferId = gl.createBuffer();
  gl.bindBuffer( gl.ARRAY_BUFFER, bufferId );
  gl.bufferData( gl.ARRAY_BUFFER, flatten(data), gl.STATIC_DRAW );

  // associate shader variables with data buffer
  var vPosition = gl.getAttribLocation( program, 'vPosition' );
  gl.vertexAttribPointer( vPosition, 2, gl.FLOAT, false, 0, 0 );
  gl.enableVertexAttribArray( vPosition );
};

// 使用calculateDistance()函数计算顶点到原点的长度,
// 用该长度代入公式, 计算旋转以后的新的顶点坐标.
var calculateRotation = function(vec2Point, theta) {
  // 在这里添加你的源代码
  //
  var distance = calculateDistance(vec2Point);
  var transformedPoint = [
    vec2Point[0] * Math.cos(distance * theta) - vec2Point[1] * Math.sin(distance * theta) ,
    vec2Point[0] * Math.sin(distance * theta) + vec2Point[1] * Math.cos(distance * theta)
  ];
  return transformedPoint;
};

// 完成实际的旋转工作之后, 将数据发送到GPU绘制出图形.
var doRotate = function(theta) {
    var radians = (Math.PI / 180) * theta;

    var rotatedPoints = points.map(function(vertex) {
      return calculateRotation(vertex, radians);
    });

  // 在这里添加你的源代码
  //
  loadBuffer(rotatedPoints);
  render();
};

var doDivide = function(numDivisions) {
  resetPoints();
  divideTriangle(originalTriangle[0], originalTriangle[1], originalTriangle[2], numDivisions);
  loadBuffer(points);
  render();
};

var doDivideSquare = function(numDivisions) {
  resetPoints();
  divideSquare(originalSquare[0], originalSquare[1], originalSquare[2], originalSquare[4], numDivisions);
  loadBuffer(points);
  render();
};

var updateTriangle = function(evt) {
  evt.preventDefault();
  if (evt.target.id === 'gasket') {
    if (document.getElementById('gasket').checked) {
      _gasket = true;
    } else {
      _gasket = false;
    }
  }
  if (document.getElementById('squareShape').checked) {
    document.getElementById('gasketGroup').style.visibility = 'hidden';
    doDivideSquare(document.getElementById('numDivisions').valueAsNumber);
  }
  if (document.getElementById('triangleShape').checked) {
    document.getElementById('gasketGroup').style.visibility = '';
    doDivide(document.getElementById('numDivisions').valueAsNumber);
  }
  var fills = document.getElementsByName('fill');
  for (var i = 0; i < fills.length; i++) {
    if (fills[i].checked) {
        _fill = fills[i].value;
        break;
    }
  }
  doRotate(document.getElementById('theta').valueAsNumber);
};

var doReset = function(evt) {
  evt.preventDefault();
  resetPoints();
  _gasket = false;
  _fill = 'solid';
  divideTriangle(originalTriangle[0], originalTriangle[1], originalTriangle[2], numDivisions);
  doRotate(_initialRotation);
  document.getElementById('theta').value = _initialRotation;
  document.getElementById('thetaValue').value = _initialRotation;
  document.getElementById('numDivisions').value = 4;
  document.getElementById('numDivisionsValue').value = 4;
  document.getElementById('gasket').checked = false;
  document.getElementById('gasketGroup').style.visibility = '';
  document.getElementById('triangleShape').checked = true;
  document.getElementById('fillSolid').checked = true;
  document.getElementById('gasketGroup').style.display = '';
};

window.onload = function init() {

  // register event handlers
  document.getElementById('settings').addEventListener('change', updateTriangle);
  document.getElementById('reset').addEventListener('click', doReset);

  // init
  var canvas = document.getElementById( 'gl-canvas' );
  gl = WebGLUtils.setupWebGL( canvas );
  if ( !gl ) { alert( 'WebGL isn\'t available' ); }

  // configure display
  gl.viewport( 0, 0, canvas.width, canvas.height );
  gl.clearColor( 0.3176, 0.3058, 0.3058, 1.0 );

  // load shaders
  program = initShaders( gl, 'vertex-shader', 'fragment-shader' );
  gl.useProgram( program );

  // Generate tasselated triangle data (modifies global points array)
  divideTriangle(originalTriangle[0], originalTriangle[1], originalTriangle[2], numDivisions);

  // Rotate the shape and render
  doRotate(_initialRotation);
};


实验报告

实验报告一