-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample03.js
More file actions
355 lines (281 loc) · 12.3 KB
/
example03.js
File metadata and controls
355 lines (281 loc) · 12.3 KB
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// This is where the entrypoint is
main();
// Mozilla's tutorial on WebGL was used as a base for this example.
// See https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial
// The original source is here: https://github.com/mdn/webgl-examples
function main() {
console.log("Setting up the canvas");
// Find the canavas tag in the HTML document
const canvas = document.querySelector("#exampleCanvas");
// Initialize the WebGL2 context
// The `gl` variable represents the context for which we access WebGL.
// By calling it `gl`, the calls are similar in syntax to the OpenGL ES API.
const gl = canvas.getContext("webgl2");
// Only continue if WebGL2 is available and working
if (gl === null) {
printError('WebGL 2 not supported by your browser',
'Check to see you are using a <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API#WebGL_2_2" class="alert-link">modern browser</a>.');
return;
}
console.log("Initializing program");
// Create a state for our scene
var state = {
camera: {
position: vec3.fromValues(5.0, 5.0, 5.0),
center: vec3.fromValues(0.0, 0.0, 0.0),
up: vec3.fromValues(0.0, 1.0, 0.0),
},
canvas: canvas,
};
function onLoaderLoad(mesh) {
// Initialize shader
var programInfo = intializeShaderProgram(gl);
var meshVertices = new Float32Array(mesh.vertices);
var meshNormals = new Float32Array(mesh.normals);
var buffers = initBuffers(gl, programInfo,
meshVertices,
meshNormals,
);
var maxPos = Math.max(Math.max.apply(null, meshVertices), Math.abs(Math.min.apply(null, meshVertices)));
console.log("Starting rendering loop");
// A variable for keeping track of time between frames
var then = 0.0;
// This function is called when we want to render a frame to the canvas
function render(now) {
now *= 0.001; // convert to seconds
const deltaTime = now - then;
then = now;
// Rotate the camera
state.camera.position = vec3.fromValues(Math.sin(now) * (maxPos + 10.0), maxPos + 5.0, Math.cos(now) * (maxPos + 10.0));
// Draw our scene
drawScene(gl, deltaTime, programInfo, buffers, state);
// Request another frame when this one is done
requestAnimationFrame(render);
}
requestAnimationFrame(render); // Draw the scene
}
// Hook up the button
const fileSubmitButton = document.querySelector("#fileSubmitButton");
fileSubmitButton.addEventListener("click", () => {
console.log("Submitting file...");
let fileInput = document.getElementById('objInputFile');
let files = fileInput.files;
let url = URL.createObjectURL(files[0]);
fetch(url, {
mode: 'no-cors' // 'cors' by default
}).then(res=>{return res.text();})
.then(data => {
console.log("Parsing file...");
mesh = OBJLoader.prototype.parse(data);
onLoaderLoad(mesh);
});
});
}
/**
* Draws the scene. Should be called every frame
*
* @param {} gl WebGL2 context
* @param {number} deltaTime Time between each rendering call
* @param {} programInfo Custom object containing shader program and locations
* @param {} buffers Buffer data to use to draw shapes
* @param {} state State of objects in the scene
*/
function drawScene(gl, deltaTime, programInfo, buffers, state) {
// Set clear colour
// This is a Red-Green-Blue-Alpha colour
// See https://en.wikipedia.org/wiki/RGB_color_model
// Here we use floating point values. In other places you may see byte representation (0-255).
gl.clearColor(0.55686, 0.54902, 0.52157, 1.0);
// Depth testing allows WebGL to figure out what order to draw our objects such that the look natural.
// We want to draw far objects first, and then draw nearer objects on top of those to obscure them.
// To determine the order to draw, WebGL can test the Z value of the objects.
// The z-axis goes out of the screen
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl.clearDepth(1.0); // Clear everything
gl.disable(gl.CULL_FACE);
// Clear the color and depth buffer with specified clear colour.
// This will replace everything that was in the previous frame with the clear colour.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Choose to use our shader
gl.useProgram(programInfo.program);
{
// Update uniforms
var projectionMatrix = mat4.create();
var fovy = 60.0 * Math.PI / 180.0; // Vertical field of view in radians
var aspect = state.canvas.clientWidth / state.canvas.clientHeight; // Aspect ratio of the canvas
var near = 0.1; // Near clipping plane
var far = 10000.0; // Far clipping plane
// Generate the projection matrix using perspective
mat4.perspective(projectionMatrix, fovy, aspect, near, far);
gl.uniformMatrix4fv(programInfo.uniformLocations.projection, false, projectionMatrix);
var viewMatrix = mat4.create();
mat4.lookAt(
viewMatrix,
state.camera.position,
state.camera.center,
state.camera.up,
);
gl.uniformMatrix4fv(programInfo.uniformLocations.view, false, viewMatrix);
gl.uniformMatrix4fv(programInfo.uniformLocations.model, false, mat4.create());
// Bind the buffer we want to draw
gl.bindVertexArray(buffers.vao);
// Draw the object
const offset = 0; // Number of elements to skip before starting
gl.drawArrays(gl.TRIANGLES, offset, buffers.numVertices);
}
}
function intializeShaderProgram(gl){
// Vertex shader source code
const vsSource =
`#version 300 es
in vec3 aPosition;
in vec3 aNormal;
uniform mat4 uProjectionMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uModelMatrix;
out vec4 oColor;
void main() {
// Position needs to be a vec4 with w as 1.0
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition, 1.0);
// Pass the colour to the fragment shader
oColor = abs(vec4(aNormal, 1.0));
//oColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
// Fragment shader source code
const fsSource =
`#version 300 es
precision highp float;
in vec4 oColor;
out vec4 fragColor;
void main() {
fragColor = oColor;
}
`;
// Create our shader program with our custom function
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// Collect all the info needed to use the shader program.
const programInfo = {
// The actual shader program
program: shaderProgram,
// The attribute locations. WebGL will use there to hook up the buffers to the shader program.
// NOTE: it may be wise to check if these calls fail by seeing that the returned location is not -1.
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'aPosition'),
vertexNormal: gl.getAttribLocation(shaderProgram, 'aNormal'),
},
uniformLocations: {
projection: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
view: gl.getUniformLocation(shaderProgram, 'uViewMatrix'),
model: gl.getUniformLocation(shaderProgram, 'uModelMatrix'),
}
};
// Check to see if we found the locations of our uniforms and attributes
// Typos are a common source of failure
if (programInfo.attribLocations.vertexPosition === -1 ||
programInfo.attribLocations.vertexNormal === -1 ||
programInfo.uniformLocations.projection === -1 ||
programInfo.uniformLocations.view === -1 ||
programInfo.uniformLocations.model === -1 ) {
printError('Shader Location Error', 'One or more of the uniform and attribute variables in the shaders could not be located');
}
return programInfo;
}
/**
* Initialize our buffer
*
* @param {} gl WebGL2 context
* @param {} programInfo Custom object containing shader program and locations
* @returns {} An object containing the buffers
*/
function initBuffers(gl, programInfo, positionArray, normalArray) {
// Allocate and assign a Vertex Array Object to our handle
var vertexArrayObject = gl.createVertexArray();
// Bind our Vertex Array Object as the current used object
gl.bindVertexArray(vertexArrayObject);
return {
vao: vertexArrayObject,
attributes: {
position: initPositionAttribute(gl, programInfo, positionArray),
normals: initNormalAttribute(gl, programInfo, normalArray),
},
numVertices: positionArray.length / 3,
};
}
function initPositionAttribute(gl, programInfo, positionArray) {
// Create a buffer for the positions.
const positionBuffer = gl.createBuffer();
// Select the buffer as the one to apply buffer
// operations to from here out.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Now pass the list of positions into WebGL to build the
// shape. We do this by creating a Float32Array from the
// JavaScript array, then use it to fill the current buffer.
gl.bufferData(
gl.ARRAY_BUFFER, // The kind of buffer this is
positionArray, // The data in an Array object
gl.STATIC_DRAW // We are not going to change this data, so it is static
);
// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
{
const numComponents = 3; // pull out 3 values per iteration, ie vec3
const type = gl.FLOAT; // the data in the buffer is 32bit floats
const normalize = false; // don't normalize between 0 and 1
const stride = 0; // how many bytes to get from one set of values to the next
// Set stride to 0 to use type and numComponents above
const offset = 0; // how many bytes inside the buffer to start from
// Set the information WebGL needs to read the buffer properly
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset
);
// Tell WebGL to use this attribute
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexPosition);
}
return positionBuffer;
}
function initNormalAttribute(gl, programInfo, normalArray) {
// Create a buffer for the positions.
const normalBuffer = gl.createBuffer();
// Select the buffer as the one to apply buffer
// operations to from here out.
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
// Now pass the list of positions into WebGL to build the
// shape. We do this by creating a Float32Array from the
// JavaScript array, then use it to fill the current buffer.
gl.bufferData(
gl.ARRAY_BUFFER, // The kind of buffer this is
normalArray, // The data in an Array object
gl.STATIC_DRAW // We are not going to change this data, so it is static
);
// Tell WebGL how to pull out the positions from the position
// buffer into the vertexPosition attribute.
{
const numComponents = 3; // pull out 4 values per iteration, ie vec4
const type = gl.FLOAT; // the data in the buffer is 32bit floats
const normalize = false; // don't normalize between 0 and 1
const stride = 0; // how many bytes to get from one set of values to the next
// Set stride to 0 to use type and numComponents above
const offset = 0; // how many bytes inside the buffer to start from
// Set the information WebGL needs to read the buffer properly
gl.vertexAttribPointer(
programInfo.attribLocations.vertexNormal,
numComponents,
type,
normalize,
stride,
offset
);
// Tell WebGL to use this attribute
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexNormal);
}
return normalBuffer;
}