目录

WebGL 模型 视图 投影

目录

WebGL 代码片段

CSS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

.group{
  position:absolute;
}

.group label{
  color:white;
}

.group canvas{
 position:absolute;
}

.group .slidecontainer{
 position:absolute; 
}

HTML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script>

var MDN =window.parent.MDN

</script>

<div class="group">
<canvas id="canvas"></canvas>
<div class="slidecontainer">
  <label for="slider">W</label>
  <input id="slider" type="range" step="0.1" min="0.5" max="10" value="1" class="slider" id="myRange">
</div>
<div>

JS:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
var w  = 1.0

document.getElementById("slider").addEventListener('change',(event)=>{
  w = event.target.value
  box.draw({

  top    : 0.5,             // x
  bottom : -0.5,            // x
  left   : -0.5,            // y
  right  : 0.5,             // y
  w      : w,             // w - 放大这个盒子

  depth  : 0,               // z
  color  : [1, 0.4, 0.4, 1] // red
});

box.draw({

  top    : 0.9,             // x
  bottom : 0,               // x
  left   : -0.9,            // y
  right  : 0.9,             // y
  w      : w,             // w - 放大这个盒子

  depth  : 0.5,             // z
  color  : [0.4, 1, 0.4, 1] // green
});

box.draw({

  top    : 1,               // x
  bottom : -1,              // x
  left   : -1,              // y
  right  : 1,               // y
  w      : w,             // w - 放大这个盒子

  depth  : 0.5,            // z
  color  : [0.4, 0.4, 1, 1] // blue
});
});

function WebGLBox() {

  // 设置 canvas 和 WebGL 上下文
  this.canvas = document.getElementById('canvas');
  // let width = d3.select("#content").node().getBoundingClientRect().width
  this.canvas.width = 800;
  this.canvas.height = 400;
  this.gl = MDN.createContext(canvas);

  var gl = this.gl;

  // 设置一个 WebGL 程序,任何 MDN 对象相关的部分在本文之外定义

  var vertexShader = `
    // The individual position vertex
    attribute vec4 position;

    void main() {
      
      // the gl_Position is the final position in clip space after the vertex shader modifies it
      gl_Position = position;
    }
  `

  var fragmentShader = `
    precision mediump float;
    uniform vec4 color;
    
    void main() {
      gl_FragColor = color;
    }
  `
  this.webglProgram = MDN.createWebGLProgramFromContent(gl,vertexShader,fragmentShader);
  gl.useProgram(this.webglProgram);

  // 保存 attribute 和 uniform 位置
  this.positionLocation = gl.getAttribLocation(this.webglProgram, 'position');
  this.colorLocation = gl.getUniformLocation(this.webglProgram, 'color');

  // 告诉 WebGL 在绘制时测试深度,所以如果一个正方形后面有另一个正方形
  // 另一个正方形不会被绘制
  gl.enable(gl.DEPTH_TEST);

}

WebGLBox.prototype.draw = function(settings) {

  // 创建一下 attribute 数据; 这些是最终绘制到屏幕上的三角形
  // 有两个形成一个正方形

 var data = new Float32Array([

  //Triangle 1
  settings.left,  settings.bottom, settings.depth, settings.w,
  settings.right, settings.bottom, settings.depth, settings.w,
  settings.left,  settings.top,    settings.depth, settings.w,

  //Triangle 2
  settings.left,  settings.top,    settings.depth, settings.w,
  settings.right, settings.bottom, settings.depth, settings.w,
  settings.right, settings.top,    settings.depth, settings.w
 ]);

  // 使用 WebGL 将其绘制到屏幕上

  // 性能要点:为每个绘制创建新的缓冲器很慢
  // 这个方法仅用于说明

  var gl = this.gl;

  // 创建一个缓冲区并绑定数据
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  // 设置指向 attribute 数据的指针(三角形)
  gl.enableVertexAttribArray(this.positionLocation);
  gl.vertexAttribPointer(this.positionLocation, 4, gl.FLOAT, false, 0, 0);

  // 设置将在所有三角形之间共享的 color uniform
  gl.uniform4fv(this.colorLocation, settings.color);

  // 在屏幕上绘制该三角形
  gl.drawArrays(gl.TRIANGLES, 0, 6);

}

var box = new WebGLBox();

box.draw({

  top    : 0.5,             // x
  bottom : -0.5,            // x
  left   : -0.5,            // y
  right  : 0.5,             // y
  w      : w,             // w - 放大这个盒子

  depth  : 0,               // z
  color  : [1, 0.4, 0.4, 1] // red
});

box.draw({

  top    : 0.9,             // x
  bottom : 0,               // x
  left   : -0.9,            // y
  right  : 0.9,             // y
  w      : w,             // w - 放大这个盒子

  depth  : 0.5,             // z
  color  : [0.4, 1, 0.4, 1] // green
});

box.draw({

  top    : 1,               // x
  bottom : -1,              // x
  left   : -1,              // y
  right  : 1,               // y
  w      : w,             // w - 放大这个盒子

  depth  : -1.5,            // z
  color  : [0.4, 0.4, 1, 1] // blue
});
运行结果:

Cube 代码片段

HTML:

1
2
3
4
5
<script>
var MDN =window.parent.MDN
</script>

<canvas id="canvas"></canvas>

JS:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

function CubeDemo () {
  
  // Prep the canvas
  this.canvas = document.getElementById("canvas");
  this.canvas.width = 800;
  this.canvas.height = 400;
  
  // Grab a context
  this.gl = MDN.createContext(this.canvas);

  this.transforms = {}; // All of the matrix transforms
  this.locations = {}; //All of the shader locations
  
  // Get the rest going
  this.buffers = MDN.createBuffersForCube(this.gl, MDN.createCubeData() );
  this.webglProgram = this.setupProgram();
  
}

CubeDemo.prototype.setupProgram = function() {
  
  var gl = this.gl;
    
  // Setup a WebGL program

  var vertexShader = `
    // Each point has a position and color
    attribute vec3 position;
    attribute vec4 color;
    
    // The transformation matrix
    uniform mat4 model;
    uniform mat4 projection;
    uniform mat4 view;

    // Pass the color attribute down to the fragment shader
    varying vec4 vColor;

    void main() {
      
      //Pass the color down to the fragment shader
      vColor = color;
      
      // Multiply the 
      gl_Position = projection *view *model * vec4(position, 1.0);
    }
  `

  var fragmentShader = `
    precision mediump float;
    varying vec4 vColor;
    
    void main() {
      gl_FragColor = vColor;
    }
  `
  var webglProgram = MDN.createWebGLProgramFromContent(gl,vertexShader,fragmentShader);
  gl.useProgram(webglProgram);
  
  // Save the attribute and uniform locations
  this.locations.model = gl.getUniformLocation(webglProgram, "model");
  this.locations.view = gl.getUniformLocation(webglProgram, "view");
  this.locations.projection = gl.getUniformLocation(webglProgram, "projection");
  this.locations.position = gl.getAttribLocation(webglProgram, "position");
  this.locations.color = gl.getAttribLocation(webglProgram, "color");
  
  // Tell WebGL to test the depth when drawing
  gl.enable(gl.DEPTH_TEST);
  
  return webglProgram;
};

CubeDemo.prototype.computePerspectiveMatrix = function() {
  
  var fieldOfViewInRadians = Math.PI * 0.2;
  var aspectRatio = window.innerWidth / window.innerHeight;
  var nearClippingPlaneDistance = 1;
  var farClippingPlaneDistance = 100;
  
  this.transforms.projection = MDN.perspectiveMatrix(
    fieldOfViewInRadians,
    aspectRatio,
    nearClippingPlaneDistance,
    farClippingPlaneDistance
  );
};

CubeDemo.prototype.computeViewMatrix = function( now ) {

  var moveInAndOut = 20 * Math.sin(now * 0.002);
  var moveLeftAndRight = 15 * Math.sin(now * 0.0017);
  
  // Move the camera around
  var position = MDN.translateMatrix(moveLeftAndRight, 0, 50 + moveInAndOut );
  
  // Multiply together, make sure and read them in opposite order
  var matrix = MDN.multiplyArrayOfMatrices([
    
    //Exercise: rotate the camera view
    position
  ]);
  
  // Inverse the operation for camera movements, because we are actually
  // moving the geometry in the scene, not the camera itself.
  this.transforms.view = MDN.invertMatrix( matrix );
};

CubeDemo.prototype.computeModelMatrix = function( now ) {

  //Scale up
  var scale = MDN.scaleMatrix(5, 5, 5);
  
  // Rotate a slight tilt
  var rotateX = MDN.rotateXMatrix( Math.PI * 0.2 );
  
  // Rotate according to time
  var rotateY = MDN.rotateYMatrix( Math.PI * 0.2 );

  // Move slightly down
  var position = MDN.translateMatrix(0, 0, 0);
  
  // Multiply together, make sure and read them in opposite order
  this.transforms.model = MDN.multiplyArrayOfMatrices([
    position, // step 4
    rotateY,  // step 3
    rotateX,  // step 2
    scale     // step 1
  ]);
  
  
  // Performance caveat: in real production code it's best not to create
  // new arrays and objects in a loop. This example chooses code clarity
  // over performance.
};

CubeDemo.prototype.draw = function() {
  
  var gl = this.gl;
  var now = Date.now();
  
  // Compute our matrices
  this.computeModelMatrix( now );
  this.computeViewMatrix( now );
  this.computePerspectiveMatrix( 0.5 );
  
  // Update the data going to the GPU
  this.updateAttributesAndUniforms();
  
  // Perform the actual draw
  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
  
  // Run the draw as a loop
  requestAnimationFrame( this.draw.bind(this) );
};

CubeDemo.prototype.updateAttributesAndUniforms = function() {

  var gl = this.gl;
  
  // Setup the color uniform that will be shared across all triangles
  gl.uniformMatrix4fv(this.locations.model, false, new Float32Array(this.transforms.model));
  gl.uniformMatrix4fv(this.locations.projection, false, new Float32Array(this.transforms.projection));
  gl.uniformMatrix4fv(this.locations.view, false, new Float32Array(this.transforms.view));
  
  // Set the positions attribute
  gl.enableVertexAttribArray(this.locations.position);
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.positions);
  gl.vertexAttribPointer(this.locations.position, 3, gl.FLOAT, false, 0, 0);
  
  // Set the colors attribute
  gl.enableVertexAttribArray(this.locations.color);
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.colors);
  gl.vertexAttribPointer(this.locations.color, 4, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.elements );
  
};

var cube = new CubeDemo();

cube.draw();
运行结果:

原文