实验内容
本次实验的目的是对三角形和正方形做扭曲与拉伸的变形.
完整代码
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 & 拉伸 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);
};