diff --git a/package-lock.json b/package-lock.json index 9fa8a16..121199b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1288,6 +1288,15 @@ "@types/range-parser": "*" } }, + "@types/jquery": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.4.tgz", + "integrity": "sha512-//9CHhaUt/rurMJTxGI+I6DmsNHgYU6d8aSLFfO5dB7+10lwLnaWT0z5GY/yY82Q/M+B+0Qh3TixlJ8vmBeqIw==", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", @@ -1353,6 +1362,12 @@ "@types/node": "*" } }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.1.tgz", @@ -3694,6 +3709,11 @@ } } }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 0379d11..e08471b 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@babel/preset-typescript": "^7.12.7", "@babel/runtime-corejs3": "^7.12.5", "@types/express": "^4.17.9", + "@types/jquery": "^3.5.4", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@typescript-eslint/eslint-plugin": "^4.8.1", @@ -63,6 +64,7 @@ "ejs": "^3.1.5", "express": "^4.17.1", "gl-mat4": "^1.2.0", + "jquery": "^3.5.1", "react": "^17.0.1", "react-dom": "^17.0.1" } diff --git a/public/css/style.css b/public/css/style.css index 21329a9..469475b 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -14,5 +14,10 @@ body { } #glCanvas { + display: block; + max-width: 100%; +} + +#ui { display: inline-block; } \ No newline at end of file diff --git a/src/client/buffers.ts b/src/client/buffers.ts new file mode 100644 index 0000000..7ab285a --- /dev/null +++ b/src/client/buffers.ts @@ -0,0 +1,91 @@ + +/** + * init buffers to create a square + * @param {any} gl the web gl context + * @param {Array} positions the position buffer to be loaded + * @param {Array} indices the index buffer to be loaded + * @param {Array} normals the normal buffer to be loaded + * @return {any} the buffers + */ +export function initBuffers( + gl: any, + positions: Array, + indices: Array, + normals: Array) { + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array(positions), + gl.STATIC_DRAW); + + const normalBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array(normals), + gl.STATIC_DRAW); + + const myColors = [ + [1.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + [1.0, 1.0, 0.0, 1.0], + [1.0, 0.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 1.0, 1.0], + ]; + + let faceColors: any = []; + + for (let i = 0; i < 70; i++) { + faceColors = faceColors.concat(myColors); + } + // Convert the array of colors into a table for all the vertices. + + const colors: any = []; + + for (let j = 0; j < indices.length; ++j) { + const c = [ + myColors[Math.floor(Math.random() * 8)], + faceColors[Math.floor(Math.random() * 8)], + faceColors[Math.floor(Math.random() * 8)], + faceColors[Math.floor(Math.random() * 8)], + ]; + // Repeat each color four times for the four vertices of the face + // colors = colors.concat([1.0, 1.0, 1.0, 1.0]); + colors.push(c[0][0]); + colors.push(c[0][1]); + colors.push(c[0][2]); + colors.push(c[0][3]); + } + + const colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); + + const indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array(indices), gl.STATIC_DRAW); + // Now send the element array to GL + + return { + position: positionBuffer, + color: colorBuffer, + indices: indexBuffer, + normals: normalBuffer, + }; +} + +/** + * init buffers to create a square + * @param {any} gl the web gl context + * @param {any} buffers the buffers to delete + */ +export function deleteBuffers(gl: any, buffers: any) { + for (const buffer of Object.entries(buffers)) { + gl.deleteBuffer(buffer); + } +} diff --git a/src/client/changeshader.ts b/src/client/changeshader.ts new file mode 100644 index 0000000..aca1e91 --- /dev/null +++ b/src/client/changeshader.ts @@ -0,0 +1,44 @@ +import {initShaderProgram, + unlinkShaderProgram} from './shaders'; + +/** + * + * @param {any} gl the gl context + * @param {any} shaderProgram the shader program + * @param {any} fragmentShader the current fragment shader + * @param {string} fsSource new fragment shader source + * @param {string} vsSource current vsSource + * @return {any} the program info object + */ +export function changeFragmentShader(gl: any, + shaderProgram: any, + fragmentShader: any, + fsSource: string, + vsSource: string) { + shaderProgram = unlinkShaderProgram(gl, + fragmentShader, + shaderProgram); + [shaderProgram, fragmentShader] = initShaderProgram(gl, + vsSource, + fsSource)!; + const programInfo = { + program: shaderProgram, + attribLocations: { + vertexPosition: gl.getAttribLocation(shaderProgram, + 'aVertexPosition'), + vertexColor: gl.getAttribLocation(shaderProgram, + 'aVertexColor'), + vertexNormal: gl.getAttribLocation(shaderProgram, + 'aVertexNormal'), + }, + uniformLocations: { + projectionMatrix: gl.getUniformLocation( + shaderProgram, 'uProjectionMatrix'), + viewMatrix: gl.getUniformLocation( + shaderProgram, 'uviewMatrix'), + modelMatrix: gl.getUniformLocation( + shaderProgram, 'umodelMatrix'), + }, + }; + return programInfo; +} diff --git a/src/client/main.ts b/src/client/main.ts index fb5b693..abc42c3 100644 --- a/src/client/main.ts +++ b/src/client/main.ts @@ -2,6 +2,11 @@ import mat4 from 'gl-mat4'; // @ts-ignore import convert from './objparser'; +import $ from 'jquery'; + +import {initBuffers} from './buffers'; +import {initShaderProgram} from './shaders'; +import {changeFragmentShader} from './changeshader'; let squareRotation = 0.0; @@ -83,6 +88,42 @@ function main() { gl_FragColor = vec4((texture * diffuse * 0.9) + (texture * 0.1) + (specular * vec3(1.)), 1.0); }`; + const fsSource2 = ` + precision highp float; + + varying highp vec4 vColor; + varying highp vec4 vNormal; + varying highp vec3 vPosition; + + vec3 extremize(vec3 v, float n) { + if (v.x > n / 2.) + v.x = n; + else + v.x = 0.; + if (v.y > n / 2.) + v.y = n; + else + v.y = 0.; + if (v.z > n / 2.) + v.z = n; + else + v.z = 0.; + return v; + } + + void main() { + vec3 n = normalize(vec3(-500., 1000., 500.) - vPosition); + float diffuse = max(dot(vNormal.xyz, n), 0.); + float specular = pow( + max(dot( + reflect(n, vNormal.xyz), + normalize(vec3(0., 0., -50.) - vPosition)), + 0.), 10.); + vec3 tmp = extremize(mod(vPosition.xyz + vec3(100.), vec3(3.)), 3.); + vec3 texture = vec3(tmp.x / 3., tmp.y / 3., tmp.z / 3.); + gl_FragColor = vec4((texture * diffuse * 0.9) + (texture * vec3(0.1)) + (specular * vec3(1.)), 1.0); + }`; + /* eslint-enable */ fetch('/static/objs/teapot_normals.obj').then((response) => { @@ -96,8 +137,8 @@ function main() { ] = convert(data); console.log(uvs); console.log(squareRotation); - const normals = []; - const positions = []; + const normals: any = []; + const positions: any = []; for (let i = 0; i < convertedNormals.length; i++) { if (i % 4 != 0) { normals.push(convertedNormals[i]); @@ -108,9 +149,11 @@ function main() { positions.push(convertedPositions[i]); } } - const shaderProgram = initShaderProgram(gl, vsSource, fsSource); + const [shaderProgram, fragmentShader]: any = initShaderProgram(gl, + vsSource, + fsSource); - const programInfo: any = { + let programInfo: any = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, @@ -132,6 +175,7 @@ function main() { const buffers = initBuffers(gl, positions, indices, normals); let then = 0; + let changed = false; /** * Draws the scene repeatedly @@ -140,142 +184,28 @@ function main() { function render(now: any) { now *= 0.001; const deltaTime = now - then; + if (now >= 1 && changed == false) { + changed = true; + } then = now; drawScene(gl, programInfo, buffers, deltaTime, indices.length); requestAnimationFrame(render); } + + $(function() { + $('#button1').on('click', function() { + programInfo = changeFragmentShader(gl, + shaderProgram, fragmentShader, fsSource, vsSource); + }); + $('#button2').on('click', function() { + programInfo = changeFragmentShader(gl, + shaderProgram, fragmentShader, fsSource2, vsSource); + }); + }); requestAnimationFrame(render); }); } -/** - * Initialize a shader program, so WebGL knows how to draw our data - * @param {any} gl the WebGL context - * @param {string} vsSource the vertex shader source - * @param {string} fsSource the fragment shader source - * @return {any} the shader program - */ -function initShaderProgram(gl: any, vsSource: string, fsSource: string) { - const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); - const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); - - // Create the shader program - const shaderProgram = gl.createProgram(); - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - // If creating the shader program failed, alert - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - alert('Unable to initialize the shader program: ' + - gl.getProgramInfoLog(shaderProgram)); - return null; - } - return shaderProgram; -} - -/** - * load a GL shader - * @param {any} gl the WebGL context - * @param {any} type type of shader to load - * @param {string} source source code of shader - * @return {any} the loaded shader - */ -function loadShader(gl: any, type: any, source: string) { - const shader = gl.createShader(type); - // Send the source to the shader object - gl.shaderSource(shader, source); - // Compile the shader program - gl.compileShader(shader); - // See if it compiled successfully - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - alert('An error occurred compiling the shaders: ' + - gl.getShaderInfoLog(shader)); - gl.deleteShader(shader); - return null; - } - return shader; -} - -/** - * init buffers to create a square - * @param {any} gl the web gl context - * @param {Array} positions the position buffer to be loaded - * @param {Array} indices the index buffer to be loaded - * @param {Array} normals the normal buffer to be loaded - * @return {any} the buffer - */ -function initBuffers( - gl: any, - positions: Array, - indices: Array, - normals: Array) { - const positionBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array(positions), - gl.STATIC_DRAW); - - const normalBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array(normals), - gl.STATIC_DRAW); - - const myColors = [ - [1.0, 1.0, 1.0, 1.0], - [1.0, 0.0, 0.0, 1.0], - [0.0, 1.0, 0.0, 1.0], - [0.0, 0.0, 1.0, 1.0], - [1.0, 1.0, 0.0, 1.0], - [1.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 0.0, 1.0], - [0.0, 1.0, 1.0, 1.0], - ]; - - let faceColors: any = []; - - for (let i = 0; i < 70; i++) { - faceColors = faceColors.concat(myColors); - } - // Convert the array of colors into a table for all the vertices. - - const colors: any = []; - - for (let j = 0; j < indices.length; ++j) { - const c = [ - myColors[Math.floor(Math.random() * 8)], - faceColors[Math.floor(Math.random() * 8)], - faceColors[Math.floor(Math.random() * 8)], - faceColors[Math.floor(Math.random() * 8)], - ]; - // Repeat each color four times for the four vertices of the face - // colors = colors.concat([1.0, 1.0, 1.0, 1.0]); - colors.push(c[0][0]); - colors.push(c[0][1]); - colors.push(c[0][2]); - colors.push(c[0][3]); - } - - const colorBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); - - const indexBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, - new Uint16Array(indices), gl.STATIC_DRAW); - // Now send the element array to GL - - return { - position: positionBuffer, - color: colorBuffer, - indices: indexBuffer, - normals: normalBuffer, - }; -} /** * Draw a webgl scene diff --git a/src/client/shaders.ts b/src/client/shaders.ts new file mode 100644 index 0000000..13018e6 --- /dev/null +++ b/src/client/shaders.ts @@ -0,0 +1,62 @@ +/** + * Initialize a shader program, so WebGL knows how to draw our data + * @param {any} gl the WebGL context + * @param {any} shader the shader to unlink + * @param {any} shaderProgram the existing shaderprogram + * @return {any} the shader program + */ +export function unlinkShaderProgram(gl: any, shader: any, shaderProgram: any) { + // Create the shader program + gl.detachShader(shaderProgram, shader); + gl.deleteShader(shader); + return shaderProgram; +} + +/** + * Initialize a shader program, so WebGL knows how to draw our data + * @param {any} gl the WebGL context + * @param {string} vsSource the vertex shader source + * @param {string} fsSource the fragment shader source + * @return {any} the shader program + */ +export function initShaderProgram(gl: any, vsSource: string, fsSource: string) { + const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); + const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); + + // Create the shader program + const shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + // If creating the shader program failed, alert + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert('Unable to initialize the shader program: ' + + gl.getProgramInfoLog(shaderProgram)); + return null; + } + return [shaderProgram, fragmentShader]; +} + +/** + * load a GL shader + * @param {any} gl the WebGL context + * @param {any} type type of shader to load + * @param {string} source source code of shader + * @return {any} the loaded shader + */ +export function loadShader(gl: any, type: any, source: string) { + const shader = gl.createShader(type); + // Send the source to the shader object + gl.shaderSource(shader, source); + // Compile the shader program + gl.compileShader(shader); + // See if it compiled successfully + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert('An error occurred compiling the shaders: ' + + gl.getShaderInfoLog(shader)); + gl.deleteShader(shader); + return null; + } + return shader; +} diff --git a/views/index.ejs b/views/index.ejs index 40f1139..d7cad2f 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -13,6 +13,10 @@
+
+ + +